본문 바로가기
개발개발/c++

c++의 구조, 문자열 출력, 변수, 상수, 형변환

by 유잉유잉유잉 2024. 12. 4.
728x90

▶️  언어는 고수준언어와 저수준 언어가 있습니다.

  • 고수준언어 : 사람이 이해하기 편한 언어 ex) 자바 C# 등
  • 저수준언어 : 컴퓨터가 이해하기 편한 언어 ex)어셈블리어
  • 컴파일러 : 수준 언어를 저수준 언어로 번역해 주기 위한 번역기.
  • 전처리기 : 컴파일 전에 처리하는 기능입니다.
    • 다른 cpp 파일도 포함가능하며 헤더 파일을 포함시켜서 사용하기 위한 기능입니다. 대부분 헤더파일 포함하기 위해 사용합니다.
    • ex : #include <iostream>
    • #define
  • include : 헤더 파일을 포함시켜서 사용하기 위한 기능입니다.

 

 

▶️ 실행 파일이 만들어지는 순서 

코드 작성 -> 전처리 -> 컴파일 -> 빌드 -> 링크 -> 실행파일 제작 

  • 전처리 (Preprocessing) : 전처리기 구문을 전처리기로 컴파일 전 코드를 준비하고 처리하는 과정.
    • 매크로 치환작업이나 전처리 구문으로 필요한 헤더 파일을 불러오고 define으로 정의한 기호 상수를 코드상에 채워 넣어주는 역할등을 진행합니다.
  • 컴파일(Compilation)  : 컴파일러가 전처리기가 처리해준 소스코드를 이용해서 어셈블리어로 만들어 주는 과정을 말합니다.
  • 빌드 : 컴파일된 소스코드에 필요한 파일을 링크시켜주는것. 빌드 = 컴파일 + 링크 
  • 링크(Linking) : 컴파일된 목적파일을 실행 가능한 파일로 만들도록 연결하는 작업 입니다.
    •   링커를 통해 여러 목적파일들을 하나로 결합하여 하나의 실행파일로 만듭니다.

 

▶️  진입점 

프로젝트의 시작점 함수입니다. 콘솔 환경에선 main() 키워드는 고정입니다.

윈도우 환경에선 winMain() 입니다.

 

- 한번 빌드하고 난 후(ctrl + f5)

해당 프로젝트 파일의 debug 폴더안에 들어가보면 exe파일이 생성되어 있습니다.

실행파일을 구성하고 있는 기계어코드가 메모리에 저장되고 cpu가 동작시켜주는 방식으로 실행파일이 실행하게 됩니다.

 

 

- 코드 최상단 #include <iostream>을 넣어야 합니다.

// #이 붙을 경우 전처리기
// include : 헤더파일을 포함시켜서 사용하기 위한 기능
// iostream : 표준 입출력 지원. 파일 읽어오기, 쓰기 등등 가능 
#include <iostream>

- warning 줄이기 : 실무에선 특히 안뜨게 해야합니다.

-> 🐝🍯꿀팁 : 해당 워닝을 더블클릭하면 경고를 발생시키는 줄로 이동해줍니다.

VS 하단에서 오류목록 확인가능

 

#include <iostream>

using namespace std;

int main()
{ 
	return 0;
}

- using namespace std; // std를 사용하겠다고 네임스페이스 사용 선언

위 문장을 사용할 경우 std::를 생략할 수 있습니다.

하지만 실무에서 사용하면 네이밍 충돌이 일어날 수 있기때문에 위 문장은 추천하지 않습니다.

 

- 문자열 출력

int main()
{
	// cout : console output // dos(콘솔)창에 텍스트 출력시 사용
	// ::는 범위지정연산자 (스코필드) => std안에있는 cout
    // std는 namespace이다. => 구분을 지어서 사용할 수 있게 해줌 
	// namespace는 소속을 지정해줌. 동일한 이름이라도 소속을 지정하여 사용할 수 있게 해줍니다.
    // 오른쪽에서 왼쪽으로 해석이 진행됨
    // "" 안에 텍스트를 작성하는 것을 문자열이라고 합니다
    // 문자열은 문자 여러개의 집합입니다
    // ''안에 문자 하나를 작성할 수 있습니다 
	// => 오른쪽의 문자열을 cout으로 보내서 콘솔창에 출력해준다
    // std::endl : 개행문자를 만들어준다
    std::cout << "Output\n" << std::endl;
    
    // c언어 스타일 출력 방식
	printf("aa\n");
}

 

 

- 코드를 작성후 인텔리전스(코드가 에러가 났는지 등등을 판단해주는 기능)이 느린경우가 있습니다

ctrl + B 를 눌러서 컴파일 에러가 나는지

코드 작성 중간마다 확인해주는 습관을 기릅시다.

 

 

- 빌드 후 프로젝트 폴더안에서 일어나는 일

빌드를 하고 나면 프로젝트 폴더 안에 x64 (64비트 컴퓨터의 경우. 32비트 컴퓨터의 경우 x86으로 생성)

라는 폴더가 추가되어 있습니다.

해당 폴더안의 'Debug'폴더안에 들어가면 '프로젝트명.exe' 파일이 생성되어 있습니다.

위 코드로 작성되었을 경우 exe파일을 눌러도 실행이 되지않는것 처럼 보이지만

빠르게 실행되고 빠르게 종료되기 때문에 실행이 되지 않는것처럼 보이는것입니다.

비주얼스튜디오 상단에서 해당 옵션을 확인할 수 있습니다.

솔루션 플랫폼에서 64비트, 32비트 선택이 가능합니다

 

Debug : 개발용 단계에서 사용.

디버깅 툴이 포함. 용량이 큼. 

Release : 최종적으로 배포 버전을 만드는데 사용

-> 작업팁 : debug로 제작 후 한개의 기능 (또는 작업단위) 완성 후 Release 버전으로 빌드하여 문제가없는지 한번씩 확인하는것이 중요합니다. debug 빌드일때 안나오는 문제가 Release 빌드에서 나오는 경우가 있기 때문입니다.

 

 

 

 


 

[ 변수 ]


- 메모리에 공간을 만들고 값을 저장하기 위해 사용합니다.
- 저장된 값은 필요에 따라 변경도 가능합니다.


- 저장하는 데이터의 종류에 따라 타입이 구분됩니다.
1bit : 용량의 최소 단위입니다. 0, 1 둘 중 하나를 표현할 수 있습니다
1byte : 8bit. 0, 1 둘 중 하나를 8자리로 표현할 수 있다. 2의 8승. 256개 표현 가능합니다
1kbyte : 1024byte

- 정수/실수 타입들은 unsigned  키워드를 이용해서 -부분을 +부분으로 만들 수 있습니다. (플러스만 존재)

 

⭐  1byte의 표현범위는 외우는걸 추천드리며, 나머지는 대략적인 범위를 알아두길 추천드립니다.

⭐ 타입별 메모리크기는 외우길 추천드립니다.

타입  데이터 종류   메모리 크기  값의 표현 범위 기타
char  문자  1byte  -128 ~ 127개 (0포함) 한글이나 한문은 2byte.
⭐ 1byte 표현범위는 외우기 
bool  참 / 거짓 1byte  false(0), true(1) 거짓을 표현할 때 0은 거짓, 0이 아닌 모든 수는 참
short  정수  2byte  -32768 ~ 32767 요새는 기본이 4byte
int  정수  4byte   -2,147,483,648 ~ 2,147,483,647 약  - 22억 ~ 21억
__int64  정수  8byte  -9,223,372,036,854,775,808 
~ 9,223,372,036,854,775,807
 
long 정수 4byte -2,147,483,648 ~ 2,147,483,647 ✅메모리 크기 예외 :
Window에선 4byte,
리눅스등 os 64x에선
8byte
long long 정수 8byte -9,223,372,036,854,775,808 
~ 9,223,372,036,854,775,807
 
float  부동소수점  4byte  3.4E-38(-3.4*10^38) 
~ 3.4E+38(3.4*10^38)
 
double  부동소수점  8byte  1.79E-308(-1.79*10^308) 
~ 1.79E+308(1.79*10^308)
 

 

 

▶️ 초기화 하지 않은 변수를 사용할 경우 컴파일 에러가 날 수 있습니다.

초기화 하지 않을 경우 변수에는 쓰레기값이 들어가게 됩니다.

int num ; // 선언만 하고 초기화하지 않음 (값을 대입하지 않음)

std::cout << num << std::endl; // 변수를 초기화하지 않고 사용을 시도하여 에러

 

 

- 표현범위를 넘어가면 한바퀴돌아서 다음값으로 출력

short num = 32768;
std::cout << num << std::endl;

결과값 :

- short이 표현할 수 있는 최대 표현범위(~32767)를 넘어섰기 때문에(오버플로)

short의 표현범위 중 제일 적은 수인 -32768이 출력됩니다. 

 

short sh = -32769;
std::cout << sh << std::endl;

반대의 경우도 동일하게 나오는 것을 확인하실 수 있습니다.

 

 

- bool값을 출력하면 false는 0, true는 1로 출력됩니다 

bool testBool = false;
std::cout << testBool << std::endl;

 

 

 

 

[ 상수 ]


- 메모리에 공간을 만들고 값을 저장하기 위해 사용.
- 저장된 값은 변경이 '불가능' 합니다.

- 변수 앞에 'const'라는 키워드를 붙여주면 상수가 됩니다. 

- 선언과 동시에 초기화해주어야 합니다.

// 변수 선언 및 초기화 예시
const int numConst = 100;

// 아래 문장을 넣을 경우 에러! : 상수의 값을 변경하려 해서.
//numConst = 200;

 

 

[ 형변환 ]

- 변수 타입을 다른 타입으로 일시적으로 변경하는 기능입니다.

char testChar = 'a';
// c언어 스타일 형변환
std::cout << (int)testChar << std::endl;
// c++스타일 형변환
std::cout << static_cast<int>(testChar) << std::endl;

 

 

[ cout으로 실수 출력할때 주의사항 ]

float num3 = 3.14546546453321312f;
double num4 = 33.6654987;

// 출력 : 3.14547. cout은 최대 소수점 5개까지 출력해줌
std::cout << num3 << std::endl;

// 출력 : 33.6655. 얘는 정수앞에 자릿수가 커서리 소숫점자리수가 줄어들음
std::cout << num4 << std::endl;

- 값이 잘려보이지만 값은 그대로 가지고 있으며, 출력할때만 모든 자릿수가 출력되지 않는 상태입니다.

 

[ printf()를 사용하여 중간에 정수, 문자열, 실수 넣기 ]

// %d : 정수를 받아서 출력
// %s : 문자열을 받아서 출력
// %f : 실수를 받아서 출력
printf("Print Num : %d, %d\n", num, num2);

// 3.145465	출력	// 반올림 처리되어 출력
printf("Print Num3 : %f\n", num3); 	

// 3.145		// 소숫점 3자리까지만 출력 // %.nf 소숫점 n자리수까지 출력
printf("Print Num3 : %.3f\n", num3);

 

 

[ 실수를 정수타입 변수에 대입할 경우] : 암시적 형변환 발생

// float 타입 변수를 int 타입 변수에 대입. 형변환이 일어남. 소숫점 아래 잘림.
// 실수를 정수형 변수에 넣기
float num33 = 3.14546546453321312f;
int num6 = 0;
num6 = num33;
std::cout << num6 << std::endl; // 3 출력
  • float 타입 변수를 int 타입 변수에 대입할 경우 우리 눈에는 보이지 않지만 형변환이 일어나고 있습니다.

 

 

 

[ 실수형 변수의 앞자리수가 늘어날 경우 ]

float num3 = 1234567.0f;
std::cout << num3 << std::endl; // 1.23457e+06 출력 // cout이 이렇게 출력해버림. 수가 큰 수라면 자동으로 지수표기법으로 변경되어버림. 하지만 실제 변수의 값은 정상적으로 가지고있음.

double num4 = 1234567.0f
std::cout<< num4 << std::endl; // 위와동일

printf( "num3 is %.3f", num3 ); // num3 is 1234567.000 출력

- cout으로 표현할 경우 위와같이 지수로 표현됩니다.

=> cout에서 float은 32비트의 메모리공간을 쪼개서 사용합니다. 정수부분을 표현하는 범위가 int보다 적기때문에 주의해야 합니다.

- 디버깅으로 num3의 값을 확인하면 값은 1234567.0이 정확히 들어가있음을 확인할 수 있습니다

- printf로 출력할 경우 정상적으로 출력됨을 확인할 수 있습니다.

 

 

[ 연산자 ]

1. 사칙연산자 : + - * / %(나머지연산자, 모드연산자). *(곱하기)와 /(나누기)가 우선순위가 높아서 먼저 계산됩니다.
 10 / 3 = 3.3333
 10 % 3 = 1 
 나머지 연산자는 정수만 계산가능 합니다.

 std::cout << (1 + 5) * 3 << std::endl; // 18 출력

 

1) 나눗셈 사용시 주의점 : 0으로 나누기 시도시 에러가 납니다

ex) 10 / 0   <-------- 에러 !

그런데 0을 나누려 하면 에러 나지 않습니다.

ex) 0 / 10       // 0 출력

2) 나눗셈 사용시 주의점2 : 왼쪽 오른쪽값이 정수일 경우 결과값도 정수만 나옵니다

ex) 10 / 3       // 3 출력

-> 10.f / 3    // 3.33333 출력  // 실수가 필요할 경우, 둘 중 한개의 값은 실수로 바꿔주어야 합니다

 

 

 

2. 관계연산자 : <, >, <=,  >=, ==, !=

- 값 대 값을 연산해서 결과로 참/거짓이 나오게 됩니다.

std::cout << (10 < 20) << std::endl;
std::cout << (10 > 20) << std::endl;

 

 

 

3. 논리연산자

: 참/거짓 연산해서 결과로 참/거짓이 나옵니다.

  • OR(||) : 한개만 참이어도 참
  • AND(&&) : 둘다 참이어야 참
  • NOT(!) : 역
std::cout << "false || false = " << (false || false) << std::endl;
std::cout << "true || false = " << (true || false) << std::endl;
std::cout << "false || true = " << (false || true) << std::endl;
std::cout << "true || true = " << (true || true) << std::endl;

std::cout << "false && false = " << (false && false) << std::endl;
std::cout << "true && false = " << (true && false) << std::endl;
std::cout << "false && true = " << (false && true) << std::endl;
std::cout << "true && true = " << (true && true) << std::endl;

std::cout << "!true = " << (!true) << std::endl;
std::cout << "!false = " << (!false) << std::endl;

결과

 

- 관계 연산자가 논리 연산자보다 우선순위가 높습니다. 

num1 = 85;
std::cout << "num1이 70이상이고, 90이상인지 체크 : " << (70 <= num1 && num1 <= 90) << std::endl;

 


 

[ 2진수, 10진수, 16진수 ]

  • 2진수 : 0, 1 둘 중 하나의 값으로 표현하는 숫자의 집합
  • 10진수 : 0 ~ 9 사이의 하나의 값으로 표현하는 숫자의 집합
  • 16진수 : 0 ~ 15사이의 하나의 값으로 표현하는 숫자의 집합. 10 ~ 15 사이는 알파벳 a - f 로 표현


▶️ 10진수 471 -> 2진수로 바꾸기 

471 / 2 = 235 ---- 1
235 / 2 = 117 ---- 1
117 / 2 = 58 ----- 1
58 / 2 = 29  ----- 0
29 / 2 = 14 ------ 1
14 / 2 = 7 ------- 0
7 / 2 = 3 -------- 1
3 / 2 = 1 -------- 1
몫부터 거꾸로 읽기 : 111010111

▶️  111010111를 10진수로 바꾸기
1 1 1 0 1 0 1 1 1
256 128 64 32 16 8 4 2 1 => 위에가 1인 부분만 더워주기
= 256 + 128 + ... + 1 = 471

▶️  10 -> 16진수로 바꾸기
- 먼저 2진수로 바꿔주기
- 16진수 1자리는 2진수 4자리로 구성
1 / 1101 / 0111 <- 뒤에부터 4자리씩 쪼개기
1 / 8421 / 8421
1 / 13 / 7
1 / d / 7
0x1d7 <= 앞에 0x붙여주기 (16진수라는것 표시)

반대로
7 / 2 = 3 ---1
3 / 2 = 1 ---1
0111

13 / 2 = 6 --- 1
6 / 2 = 3 ---- 0
3 / 2 = 1 ---- 1
1101

=> 2진수라고 쓸땐 0b 를 앞으로 붙여주면 됩니다. 근데 쓸 일은 거의없습니다.
= 111010111

==> 2진수단위 연산이 빠릅니다.
==> 최적화에 연산 단위를 바꾸는방법도 있습니다.

 

 

[ 비트단위 논리연산자 ]

  • 값 대 값을 연산해서 결과로 값이 나오게 됩니다.
  • OR(|), AND(&), NOT(~), XOR(^) // NOT은 암호화할때 많 이 사용합니다.

 

OR(|) 연산 : 값이 한개라도 1이 있으면 1, 모두 0 이면 0
471 | 217

    111010111
    011011001
  | 111011111 => 479 (10)

 

AND(&) 연산 : 두 값이 모두 1이여야 1, 나머지 0

471 & 217
     111010111
  & 011011001
     011010001 => 209 (10)

 

 

XOR 연산 : 0110 서로 값이다를때만 TRUE

 1010

^0010

 1000

 

 

▶️ OR, AND, XOR 연산의 활용예시

// 활용방법
int 		buf		= 0;	
const int 	attack		= 0x1;		//   	1
const int 	defense		= 0x2;		//  	10
const int 	hp		= 0x4;		//    100
const int 	mp		= 0x8;		//   1000
const int 	exp		= 0x10;		//  10000

// attack 버프 on
// buf = 0 | 1  = 1;
// buf |= attack; 으로도 줄여서 사용 가능
buf = buf | attatck ;

// hp 버프 on
// buf = 001 | 100 = 101  = 5
buf = buf | hp ;

// exp 버프 on
// buf = 00101 | 10000 = 10101 (2) = 21 (10) ;
buf = buf | exp;

// hp 버프 off
// buf = 10101 ^ 00100 = 10001
buf = buf ^ hp;

// 버프가 켜져있는지 확인
std::cout << "attack = " 	<< ( buf & attack ) << std::endl;
std::cout << "defense = " 	<< ( buf & defense ) << std::endl;
std::cout << "hp = " 		<< ( buf & hp ) << std::endl;
std::cout << "mp = " 		<< ( buf & mp ) << std::endl;
std::cout << "exp = " 		<< ( buf & exp ) << std::endl;

0이 아닌 값이 들어갔을 경우 사용중이라고 판단합니다.

 

 


 NOT연산 
int : 32bit 
~ 00000000 00000000 00000001 11010111
   11111111 11111111 11111110 00101000 => 맨 첫자리는 부호비트
not은 거어어어의 쓰이지않음 보안에 쓰인다고 합니다

std::cout << ~471 << std::endl;

 

 

[ 시프트 연산자 ]

  • <<, >>
  • 값대 값을 연산하여 결과로 값이 나오게 됩니다.
  • cout 에서 사용하는 <<는 쉬프트연산자로 사용하는게 아니라 연산자를 재정의하여 사용하는 것입니다.
  • 빠른 곱하기 연산. 2의 n승 단위의 곱셈만 가능.

 

▶️ 왼쪽 시프트 연산
10은 2진수로 1010이다.

10 << 2 = 40 (왼쪽 쉬프트연산) (10 곱하기 2의 2승과 같음)
1010을 왼쪽으로 두칸 이동 시키란 의미 
1010 뒤에 0을 두개 붙이란 의미와 같습니다.
101000이 된다.

10 << 3 = 80 (10 곱하기 2의 3승과 같음)
1010000

=> 왼쪽 시프트 연산은 2의 n승 단위의 곱셈 입니다

 

▶️ 오른쪽 시프트 연산
80 >> 2 = 20                                                                                                                                                                                                                                                                      
1010000 을 오른쪽으로 2칸 이동하라는 의미로
1010000 오른쪽 2개의 숫자를 없얘는 것과 같습니다.
10100 

80 >>> 3 = 10 
1010

오른쪽 시프트 연산은 2의 n승 단위의 나눗셈 입니다
가독성은 좋지 않지만 속도는 빠릅니다.
그래픽스에서 자주 사용합니다. 신입에게 렌더링 맡기면 튀튀합시다

 

 

▶️ 시프트 연산 사용예시

#include <iostream>

int main()
{
	// a	|	r	|	g	|	b 형식으로 color에 값을 저장
    // 0	|	0	|	0	|	0
    unsigned int	color = 0;	// 4byte
    unsigned char	r = 255;	// 1byte
    unsigned char	g = 80;
    unsigned char	b = 102;
    unsigned char	a = 37;

    color = a;			// 0	|	0	|	0	|	a
    color = color << 8;	// 0	|	0	|	a	|	0	// 1byte = 8bit 
    color = color | r;  // 0	|	0	|	a	|	r	

    color = color << 8; // 0	|	a	|	r	|	0
    color = color | g;	// 0	|	a	|	r	|	g

    color = color << 8;	// a	|	r	|	g	|	0
    color = color | b;	// a	|	r	|	g	|	b

    std::cout << color << std::endl;

    // 뽑아내기 : & 연산 b 만 뽑아낼 경우 = color & 1111 1111
    std::cout << "b = " << (color & 0x000000ff) << std::endl; 

    // g 뽑기
    std::cout << "g = " << ((color >> 8)	& 0x000000ff) << std::endl;
    std::cout << "r = " << ((color >> 16)	& 0x000000ff) << std::endl;
    std::cout << "a = " << ((color >> 24)	& 0x000000ff) << std::endl;

    // buf = buf & attack ;
    // buf &= attack ;     // 누적 연산 가능 (사칙 다됨 관계 연산자는 다 된다고 생각하기)  
    num1 = 100;
    num1 += 30; // num1 = num1 + 30;

	return 0;
}

 

728x90

댓글