Post

[C++ Primer] Chapter 2 Variables and Basic Types

summary of C++ Primer chapter 2

  • 타입은 프로그램에서 데이터와 연산의 의미를 결정한다.

2.1 기본 내장 타입

  • C++에서 정의하는 기본 타입에는 산술 타입과 void라는 특별한 타입이 있다.
    • 산술 타입 - 문자, 정수, 부울값, 부동소수점
    • void 타입 - 연관된 값이 없고, 제한된 상황에서만 사용 가능

2.1.1 산술 타입

  • 산술 타입
    • 정수 타입 - 문자와 부울 타입을 포함한다.
    • 부동소수점 타입
  • 비트 수로 나타내는 산술 타입의 크기는 시스템에 따라 다르다.
  • bool타입은 turefalse의 진리 값을 나타낸다.
  • 부동소수점 타입은 단정도, 배정도, 확장 정밀도 값을 나타낸다.
  • 부호 있는 타입고 부호 없는 타입
    • bool과 확장 문자 탕비을 제외한 정수 타입은 부호 있는 타입이거나 부호 없는 타입이다.
    • 기본 문자 타입은 char, signed char, unsigned char 세가지로 구분 가능하다.
  • 사용할 타입 결정하기
    • 음수가 될 수 없으면 부호 없는 타입을 사용한다.
    • 정수에는 int를 사용한다.
    • 산술 표현식에는 일반적인 charbool을 쓰지 않는다.
    • 부동 소수점 연산에는 double을 주로 쓴다.

2.1.2 타입 변환

  • 타입 변환은 어떤 타입인 객체를 그와는 다른 타입을 기대하는 곳에 사용할 때 자동으로 일어난다.
  • 부호 있는 타입 객체에 범위를 벗어난 값을 대입하면 결과는 미정의이다. → 미정의는 하지 말자.
  • 부호 없는 타입을 포함한 표현식
    • 피 연산작 하나 또는 둘 모두가 부호 없는 타입이든 그렇지 않든, 부호 없는 타입에서 값을 뺄 때는 결과가 음수가 아닌지 확인해야 한다.

1.2.3 상수

  • 숫자와 같은 값은 그 값 자체로 명확하므로 상수라고 한다.
  • 정수와 부동소수점 상수
    • 0으로 시작하면 8진수, 0x나 0X로 시작하면 16진수
  • 문자열과 문자열 상수
    • 작은 따옴표로 둘러싼 문자는 char타입 상수이고, 큰따옴표로 둘러싼 문자는 문자열 상수이다. 문자열 상수의 타입은 char배열이다.
    • 컴파일러에서는 모든 문자열 상수에 널 문자를 추가한다.
    • 서로 이웃하며 공백 문자, 탭, 줄바꿈 문자로만 나뉜 두 문자열 상수는 하나로 합칠 수 있다.
  • 확장 문자열
    • 출력할 수 없는 문자열은 확장 문자열을 사용해 나타낼 수 있다. 백 슬래시로 시작한다.
  • 상수 타입 지정
    • 접두어와 접미어를 통해 상수의 타입을 지정할 수 있다.
  • 부울과 상수 포인터

2.2 변수

  • 변수는 프로그램에서 조작할 수 있는 명명한 저장 공간이다.
  • 변수를 객체라고도 한다.

2.2.1 변수 정의

1
int sum = 0, value, units_sold = 0;
  • 변수의 정의 - 타입 지정자로 시작하고 변수 이름을 쉼표로 구분해 하나 이상 쓴 다음 세미콜론으로 마친다.
  • 객체 - 이 책에서는 타입이 있는 데이터 영역
  • 초기화
    • 객체를 만들면서 값을 지정하는 것
    • 한 정의문 내에서 먼저 정의한 변수 값을 사용해 다른 변수를 초기화할 수 있다.
    • 초기화와 대입 연산은 다른 연산자이다. → 초기화는 변수를 만들 때 값을 지정하는 것이고 대입은 객체의 현재 값을 지우고 새로운 값으로 바꾸는 것이다.
1
2
3
4
int a = 0;
int a = {0};
int a{0};
int a(0);
  • 목록 초기화
    • 이러한 중괄호로 초기화하는 것을 목록 초기화라고 한다.
    • 초기 값 때문에 데이터 손실이 생길 수 있으면 컴파일러에서는 내장 타입 변수에 초기화 목록을 사용을 허용하지 않는다.
  • 기본 초기화
    • 초기 값 없이 변수를 정의하면 그 변수는 기본 초기화된다.
    • 하지만 함수 안에서 정의한 내장 타입 변수는 초기화하지 않는다. → 미정의이다. → 오류이다.
    • 클래스 대부분은 명시적으로 초기화 하지 않아도 객체를 정의할 수 있다. 하지만 어떤 클래스는 모든 객체를 명시적으로 초기화해야 한다. → 아니면 오류남

2.2.2 변수 선언과 정의

  • C++은 분리 컴파일을 지원한다 ← 프로그램을 여러 파일로 나누고, 각각을 독립적으로 컴파일할 수 있다.
  • 선언 - 프로그램에 이름을 알리는 것으로, 어딘가에 정의한 이름을 사용하려면 그 이름에 대한 선언을 포함한다.
  • 정의 - 연관된 개체를 만든다.
  • extern키워드를 사용하면 정의가 아닌 선언을 할 수 있다.
1
2
extern int i; // i를 선언하지만 정의하자 않는다.
int j;        // j를 선언하면서 정의한다.
  • 모든 내장 타입 객체는 초기화하길 권한다. 항상 해야 하는 것은 아니지만 하는 것이 좋다.
  • 같은 변수를 여러 파일에서 쓰려면 그 변수를 오직 한 파일에서만 정의하고, 다른 파일에선는 그 변수에 대한 정의가 아닌 선언을 해야 한다.
  • C++은 정적 타입 언어로, 컴파일 시점에서 타입을 검사한다.

2.2.3 식별자

  • C++에서 식별자는 문자, 숫자, 밑줄 문자로 만들 수 있다. 숫자로 시작할 순 없다. 대소문자를 구별한다. 예약어는 식별자로 사용할 수 없다.
  • 변수 이름 규칙
    • 식별자는 그 자체로 의미를 나타내야 한다.
    • 변수 이름은 보통 소문자로 쓴다.
    • 클래스는 보통 대문자로 시작한다.
    • 식별자를 여러 단어로 쓸 때에는 시각적으로 구분이 되도록한다. 스네이크 케이스나 캐맬 케이스로 쓴다.

2.2.4 이름 유효 범위

  • 유효 범위(scope)는 프로그램에서 이름이 특별한 의미를 지니는 구역이며 C++에서는 대부분의 유효 범위는 중괄호로 구분한다. 같은 이름이더라도 유효범위가 다르면 다른 개체를 참조할 수 있다.
  • 전역 유효 범위 (global scope) - 중괄호 밖에서 정의한 것
  • 구역 유효 범위 (block scope) - 중괄호 영역 안에서 정의한 함수이다. for문 안에서 정의한 함수는 for문 안에서만 쓸 수 있다.
  • 객체는 처음 사용하는 곳 가까이 정의하는 것이 좋다.
  • 중첩된 유효 범위
    • 포함된 유효범위를 내부 유효 범위(inner scope), 포함하는 유효 범위를 외부 유효 범위(outer scope)라고 한다.
    • 내부 유효 범위에서 외부 유효 범위의 이름을 사용할 수 있다. 외부 유효 범위에서 선언한 이름을 내부 유효 범위에서 다시 정의할 수도 있다.
    • 함수에서 사용하고 있거나 사용할 수도 있는 전역 변수와 같은 이름으로 지역 변수를 정의하는 것은 좋지 않다.

2.3 복합 타입

  • 복합타입 - 다른 타입을 사용해 정의한 타입이다. 여럿이 있는데 그중 참조자와 포인터를 이 장에서 다룬다.

2.3.1 참조자

  • 참조자는 객체에 별칭을 정의한다. 참조자로 참조하는 다른 타입이 곧 참조자 타입이다.
  • 참조자 타입을 정의할 때는 선언자를 &이름형식으로 쓴다.
  • 보통 변수를 초기화할 때는 초기 값을 생성하는 객체에 복사해 넣는다. 참조자를 정의할 때는 초기 값을 복사하지 않고 참조자를 초기화식에 결합한다. 참조자는 반드시 초기화해야 한다.
  • 참조자는 별칭이다
    • 참조자는 객체가 아니며, 이는 이미 존재하고 있는 객체에 대한 다른 이름일 뿐이다.
  • 참조자 정의

2.3.2 포인터

  • 포인터는 다른 타입을 가리키는 복합 타입이다.
  • 참조자와 달리 포인터는 그 자체로 객체이다.
  • 대입하거나 복사할 수 있으며, 포인터 하나가 생명 주기 동안 여러 다른 객체를 가리킬 수 있다.
  • 초기화하지 않아도 된다. → 그러면 미정의 값을 가진다.
  • *이름형식으로 쓴다, *은 각 포인터 변수마다 반복해 써야한다.
  • 객체 주소 얻기
    • 포인터는 다른 객체의 주소를 담고 있다. 객체의 주소는 주소연산자(&)를 통해 얻는다.
  • 포인터 값
    • 포인터에 저장하는 값인 주소는 다음 네 가지 상태 중 하나가 될 수 있다.
      • 객체를 가리킬 수 있다.
      • 객체의 마지막 요소 바름 다음 위치를 가리킬 수 있다.
      • 어느 객체와도 결합하지 않았음을 나타내는 널 포인터일 수 있다.
      • 유효하지 않을 수 있다. 위 3가지가 아닐 수 있다.
  • 포인터를 사용해 객체에 접근하기
    • 포인터로 객체를 가리키고 있으면 역참조 연산자(*)를 사용해 객체에 접근할 수 있다.
  • 널 포인터
    • 어느 객체도 가리키지 않으므로 포인터를 사용하기 전에 널인지 확인하는 것이 좋다.
    • 최신 C++프로그램에서는 NULL보다 nullptr을 쓰는 것이 좋다.
  • 대입과 포인터
    • 참조자는 객체가 아니다.
    • 모든 포인터는 초기화해야한다.
  • 다른 포인터 연산
    • 포인터 값이 유효하면(0이 아니면) 조건문에 쓸 수 있다. 0이면 false이다. 0이 아니면 true이다
  • void*포인터
    • void*포인터는 모든 객체의 주소를 담을 수 있는 특별한 포인터 타입이다. 가리키는 객체의 타입은 알 수 없다.
    • 일반적으로 메모리를 메모리로 다루기 위해 사용된다.

2.3.3 복합 타입 선언 이해하기

  • 여러 변수를 정의하기
    • 타입 변경자는 단일 문장에서 정의한 모든 변수에 적용하는 것이 아니다.
  • 포인터에 대한 포인터
    • 일반적으로 선언자에 적용할 수 있는 타입 변경자 수에는 제한이 없다.
  • 포인터에 대한 참조자
    • 참조자는 객체가 아니다 → 참조자에 대한 포인터는 없다.
    • 포인터는 객체이다 → 포인터에 대한 참조자는 정의할 수 있다.
    • 오른쪽에서 왼쪽으로 읽으면 쉽게 이해할 수 있다.

2.4 const 한정자

1
const int buf = 100;
  • 변수의 타입을 const로 정의해서 값이 바뀌지 않도록 할 수 있다.
  • 초기화와 const
    • 객체를 바꿀 수 없는 연산만 사용가능 하다.
  • 기본적으로 const 객체는 파일에 지역적이다.
    • const 객체를 상수로 초기화한다면 컴파일 중에 그 변수를 모두 해당 값으로 변환한다.
    • const객체를 여러 파일에서 공유하려면 그 변수를 extern으로 정의해야한다.

2.4.1 const에 대한 참조자

  • 참조자를 const타입 객체에 결합할 수 있다.
  • 보통의 참조자와 달리 const에 대한 참조자를 사용해 해당 참조자와 결합한 객체를 바꿀 수 없다.
  • const의 참조자를 만들 때에는 참조자도 const를 붙여야한다.
  • 초기화와 const에 대한 참조자
    • const에 대한 참조자는 const가 아닌 객체, 상수, 더 일반적인 표현식과 결합할 수 있다.
    • 아래 코드와 같이 컴파일러가 코드를 위에서 아래로 변경한다.
1
2
3
4
5
double dval = 3.14;
const int &ri = dval;

const int tmp = dval;
const int &ri = temp;
  • const에 대한 참조자는 const가 아닌 객체를 참조할 수 있다.
    • const를 써서 만든 참조자는 값을 변경할 수 없다. 하지만 변수는 직접 변경할 수 있다.

2.4.2 포인터와 const

  • const에 대한 포인터는 가리키는 객체를 바꿀 수 없다. const에 대한 포인터에는 const 객체의 주소만 저장할 수 있다.
  • const에 대한 포인터로 const가 아닌 객체를 가리킬 수 있다.
  • const에 대한 포인터로 객체를 가리키더라도 그 객체가 변경되지 않음을 보장하지 않는다.
  • const포인터
    • 다른 객체처럼 const 포인터는 초기화해야 하며 초기화한 후에는 담고있는 주소를 바꿀 수 없다.
    • 대상 객체를 바꿀 수 있는 지 여부는 알 수 없다.
1
2
3
4
int errNum = 0;
int *const currErr = &errNum;
const double pi = 3.14159;
const double *const pip = π

2.4.3 상위 const

  • 책의 용어 정리
    • 상위 const - 포인터 자체가 const
    • 하위 const - 가리키는 것이 const
  • 상위 const는 객체를 복사할 때 무시한다
  • 하위 const는 객체를 복사할 때 영향을 미친다
    • const가 아닌 객체는 const객체로 변환할 수 있다. 그 반대는 아니다.

2.4.4 constexpr과 상수 표현식

  • 상수 표현식은 컴파일 중에 값을 평가할 수 있으며 그 값을 바꿀 수 없는 표현식이다.
1
2
3
4
const int max_files = 20;         // O
const int limit = max_files + 1;  // O
int staff_size = 27;       // X
const int sz = get_size(); // X
  • 위 코드와 같이 상수이고 컴파일러가 초기에 값을 알 수 있을 때가 상수 표현식이다.
  • constexpr변수
    • constexpr 선언을 사용해 변수가 상수 표현식인지 컴파일러에서 확인할 수 있다. constexpr로 선언한 변수는 암시적으로 const이므로 상수표현식으로 초기화 해야한다.
1
2
3
constexpr int mf = 20;
constexpr int limit = mf + 1;
constexpr int sz = size(); // size가 constexpr함수일 때만 성립
  • 상수 타입
    • 지금까지 사용한 타입중에 산술, 참조자, 포인터 타입이 산술 타입이다. 산술 타입이 아닌 것들로 constexpr를 선언할 수 없다.
    • 포인터와 참조자 모두 constexpr로 정의할 수 있지만 초기화하는 데 사용하는 객체는 엄격히 제한된다.
  • 포인터와 constexpr
    • constexpr선언에 포인터를 정의하면 constexpr지정자는 포인터로 가리키는 타입이 아니라 포인터에 적용한다.

2.5 타입 다루기

2.5.1 타입 별칭

  • 타입 별칭 정의하기 - typedef
1
2
typedef double weges;  // weges == double
typedef weges base, *p // base = double, p = double*
  • 타입 별칭 정의하기 - using
1
using SI = Sales_item;
  • 이러한 타입 별칭은 타입 이름을 쓸 수 있는 곳 어디든지 쓸 수 있다.
  • 포인터, const와 타입 별칭
    • 별칭을 원래 타입으로 바꿔 타입 별칭을 사용하는 선언을 해석하는 것은 올바르지 않을 수 있다.

2.5.2 auto 타입 지정자

  • auto타입 지정자를 사용해 컴파일러가 타입을 알려 주도록 할 수 있다.
  • auto를 타입 지정자로 사용하는 변수에는 반드시 초기값이 있어야 한다.
  • 복합 타입, constauto
    • 참조자를 사용하면 참조자로 참조하는 객체를 실제로 사용한다.
    • auto에서는 일반적으로 상위 const절은 무시하지만 하위 const는 유지한다.
    • 추론한 타입에 상위 const가 있으려면 명시적으로 지정해야한다. const auto f = ci;

2.5.3 decltype타입 지정자

1
decltype(f()) sum = x;
  • decltype지정자는 피연산자 타입을 반환한다.
  • 컴파일러는 f를 호출헀을 때 이 함수에서 반환하게 될 타입을 sum의 타입으로 지정한다.
  • 상위 const와 참조자를 포함해 대상 변수의 타입을 반환한다.
  • decltype과 참조자
    • decltype에서 하는 추론이 대상 표현식의 형식에 의존한다.
1
2
3
int i = 42, *p = &i, &r = i;
decltype(r + 0) b; // int
decltype(*p) c; // 오류, 초기화 하지않음

2.6 데이터 구조 직접 정의하기

  • C++에서는 클래스를 정의해 자신만의 데이터 타입을 직접 정의한다.

2.6.1 Sales_data타입 정의하기

1
2
3
4
5
struct Sales_data {
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
  • 위와 같이 기본적으로 클래스를 만든다.
  • 클래스는 새 유효범위를 생성해서 다른 곳에 있는 이름을 써도 된다.
  • 클래스 데이터 멤버
    • 초기화를 할 수 있다.

2.6.2 Sales_data클래스 사용하기

  • Sales_data객체 더하기 - #include "Sales_data.h"로 선언하기
  • 데이터를 읽어 Sales_data객체에 넣기
  • Sales_data객체의 합 출력하기

2.6.3 헤더 파일 직접 만들기

  • 함수 안에 클래스를 정의하면 기능이 제한되기 때문에 파일 하나에 하나의 클래스를 정의한다.
  • 헤더는 한번이상 포함할 수 있으므로 여러 번 포함하더라도 안전하도록 해야한다.
  • 전처리기에 대한 간략한 소개
    • 전처리기 - 컴파일러를 실행하기 전에 실행해 프로그램 소스 내용을 변경하는 프로그램이다.
    • C++ 프로그램에서는 전처리기를 사용해 헤더 보호문을 정의한다. 헤더 보호문은 전처리기 변수에 의존한다.
    • 전처리기 변수의 상태는 두가지 중 하나가 될 수있다.
1
2
3
4
5
6
7
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct {
    ...
}
#endif
This post is licensed under CC BY 4.0 by the author.