1장 - C++기본 소개 - 1

1) 전처리 지시자

C++ 프로그램 빌드는 3단계를 거친다.

  1. 전처리 - 소스코드 메타 정보 처리
  2. 컴파일 - 소스코드를 머신이 읽을 수 있는 오브젝트 파일로 변환
  3. 링크 - 앞서 변환한 오브젝트 파일들을 애플리케이션으로 엮음

전처리 지시자는 #문자로 시작한다. #include <iostream>헤더 파일에 있는 내용을 현재 파일에서 사용할 수 있게 모두 가져오도록 전처리기에 지시한다.

헤더파일은 주로 나중에 소스파일에서 구현할 함수를 선언하는 용도로 사용한다. 다시말해 선언부(호출 방식, 매개변수, 매개변수타입, 리턴타입)는 헤더파일(.h)에, 구현은 소스파일(.cpp)에 작성한다.

자주 사용하는 전처리 지시자

  • include [file] → 헤더 파일 가져오기. 지정 파일 내용을 지시자 위치에 넣는다.
  • define [key], [value] → 코드에서 key에 해당하는 내용을 모두 value값으로 바꾼다.
  • ifdef [key]~endif → key값이 define으로 정의되어있다? 둘 사이의 코드블럭을 포함한다.
  • ifndef [key]~endifkey값이 define으로 정의되어있다? 둘 사이의 코드블럭을 제외한다.

    이들을 사용하여 같은 파일이 여러번 추가되는 것을 막을 수 있다.


2) 헤더 부분 차이

stdio.h, iostream처럼 뒤에 .h가 붙기도 하고 앞에 c가 붙기도 한다. 무슨 차이일까?

  • c++ 표준 라이브러리 헤더를 불러온다 → .h생략. std 혹은 하위 네임스페이스에 모두 정의되어있음.
  • c 표준 라이브러리 헤더를 사용한다 → 예전 버전 → .h확장자 표시.
  • c 표준 라이브러리 헤더를 사용한다 → 권장 버전 → 접두어 c표시.

3) Main 함수

int 값을 리턴하는 함수. 리턴 문장을 생략하면 자동으로 0을 리턴한다. 매개변수는 없거나, int main(int argc, char* argv[])형식이다.

  • argc - 프로그램에 전달할 인수의 개수
  • argv - 프로그램에 전달할 인수의 값.
  • argv[0] - 프로그램의 이름이 담긴다.

4) 네임스페이스

코드 이름 충돌 문제를 해결하기 위해 생겨났다. 함수나 메소드의 선언 뿐만 아니라 구현도 묶을 수 있다. 해당 네임스페이스를 사용하기 위해서는 네임스페이스가 있는 헤더가 필요하다. mycode라는 네임스페이스에 foo()라는 함수가 있다면, 아래 3가지 방법으로 사용할 수 있다.

  • mycode::foo()
  • namespace mycode{ foo() }
  • using namespace mycode; foo()

using 지시자는 남용하지 말 것. 특히 헤더 파일에서는 절대로 using 지시자를 사용하면 안된다. → 그 헤더를 사용하는 모든 파일에 네임스페이스가 들어간다…

중첩된 네임스페이스

  • c++ 17 이전

      namespace A{
      	namespace B{
      		namespace C{
      				/*...*/
      		}
      	}
      }
    
  • c++ 17 이후

      namespace A::B::C{
      	/*...*/
      }
        
      // Namespace Alias 기능 사용!
      namespace myC = A::B::C
    

5) 리터럴

리터럴은 코드에 표시한 숫자나 스트링과 같은 값을 의미. 참고로 숫자는 아래의 리터럴로 표시 가능하다.

  • 십진수 - 123
  • 8진수 - 0173
  • 16진수 - 0x7B
  • 2진수 - 0b1111011
  • 자릿수 구분(작은 따옴표) - 12’345’678
  • 16진수 부동소수점 = 0x3.ABCp-10 (C++17)

6) 변수

그 변수 맞다. 변수를 선언할 때 반드시 초기화 할 필요는 없지만, 에러가 발생할 수 있으니 초기화 하는 편이 좋다. 컴파일러가 잡아내기도 한다. 각 타입은 해당 블로그에 보기 좋게 정리되어 있으니 참고하자.

참고로 타입에 관하여 두 가지 알아둘 것이 있다.

  • C++ 언어 자체는 string 타입이 정의되어 있지 않다. 표준 라이브러리를 사용하자.
  • float, double같은 부동소수점 타입은 unsigned가 없다. 정수단위에서야 범위가 중요해도 부동소수점에서는 정확성이 더 중요하기 때문이다. 그리고 float만 되어도 32비트를 사용하여 자세한 범위를 나타낸다. 여기서 부호 비트를 포기하는게 큰 이익일지는 의문이다.

캐스팅

변수 타입을 실행 중에 바꿀 수 있다. 명시적 변환과 암시적 변환이 있다.

float myFloat = 3.14f를 캐스팅해보자.

  • int i1 = (int)myFloat; → C에서 사용하던 방식
  • int i2 = int(myFloat); → 거의 사용 안함
  • **int i3 = static_cast(myFloat); → 매우 명확한 방식, 권장한다!**

그리고 캐스팅 할 때는 항상 데이터 손실을 염두에 두자.


7) 타입

변수에서 설명한 타입과는 조금 다르다. 열거 타입, 엄격한 열거 타입, 구조체에 대해 알아보자.

열거 타입(enum)

열거타입은 숫자를 나열하는 방식과 범위를 마음대로 정의하여 변수를 선언하는데 활용할 수 있다. 변수에 지정할 수 있는 값의 범위를 엄격하게 제한할 수도 있다. 아래와 같이 표현하면 PieceType의 범위는 네 가지로 제한된다.

enum PieceType { PieceKing, PieceQuene, PieceRook, PiecePawn};

열거 타입의 각 멤버는 내부적으로 정수값으로 표현된다. 값을 직접 지정하지 않는다면, 0부터 순서대로 대입된다.

enum PieceType { PieceKing = 1, PieceQuene, PieceRook = 10, PiecePawn};

이런 경우에는 PieceQuene = 2, PiecePawn = 11이 대입된다. 이전 멤버의 값+1인 것이다. 어차피 전부 정수 배정이라서 모든 멤버가 선언 형태에 관계 없이 비교 가능하다.

엄격한 열거 타입(enum class)

enum은 타입을 그리 엄격하게 따지지 않는 편이다. 타입을 엄격히 따지는 것은 strong type(type sape)라고 하며, enum class가 이에 해당된다.

enum class PieceType{ King = 1, Quene, Rook= 10, Pawn};

일반 enum 이었으면 Quene, Pawn에 값이 자동으로 할당되었겠지만 enum class는 자동으로 정수 할당이 되지 않는다. 또한, 해당 멤버들은 enum class 내부에서만 유효하다. 클래스 내부에서는 King, Quene으로 사용할 수 있어도 클래스 외부에서는 PieceType::King, PieceType::Quene으로 사용해야한다.

구조체

기존에 정의된 타입을 한 개 이상 묶어서 새로운 타입으로 정의할 수 있다.

struct Information{ char firstInitial; int lastInitial; char phoneNumber; }

8) 조건문

if 조건문

ifelse ifelse로 이어지는 흐름이다. if의 조건은 다음과 같다

  • false or 0
  • true or 0이 아닌 모든 값

C++17부터는 if문 내부에 이니셜라이저를 넣을 수있다. 이니셜라이저에 쓰인 값은 <조건문>, <본문>에서만 사용 가능하다.

// if(<이니셜라이저> ; <조건문>){ <본문> } 
if(Information info = getInfo(); firstInitial != 'a'){/*...*/} 

switch 조건문

이 조건문에 지정할 수 있는 조건식은 정수, 정수 변환 가능, 열거, 엄격한 열거, 상수 타입이다. 위에서부터 차례대로 case를 검사하다가 해당되는 case의 블럭을 실행한다. break문이 나오면 빠져나오는데, 나오지 않으면 아래의 case 블럭의 실행으로 이어진다. 이렇게 case가 계속 실행되는 것을 폴스루(Fallthrough)라고 한다.

C++17부터는 컴파일러에서 폴스루 현상을 방지할 수 있다. 폴스루 현상이 나타났을 때, 실행되는 case 블럭이 비어있지 않으면 경고메세지를 발생한다. [[fallthrough]]를 입력하여 이를 방지할 수 있다.

enum ColorSet { Red, Blue, Green };

int main() {

	switch (Blue) {
	case Red:
		printf("Red");
		break;
	case Blue:
		printf("Blue");
		[[fallthrough]];
	case Green:
		printf("Green");
		[[fallthrough]];
	default:
		printf("none");
	}
}

Untitled

참고로 경고 이미지는 위와 같다.

논리연산자

C++은 다양한 논리 연산자를 지원한다. 각 사용법도 중요하지만, 알아둬야 할 것은 축약논리이다. 조건식을 평가하는 도중에 최종 결과가 확정되면 나머지 부분은 평가하지 않는 것이다. 따라서 가볍게 검사할 수 있는 부분을 앞쪽에, 무거운 부분은 뒤에 놔두는 것이 좋다.

참고자료