devseop08 님의 블로그

[Basic] 타입과 변수 본문

Language/C++

[Basic] 타입과 변수

devseop08 2025. 6. 7. 14:31

식별자 규칙

  1. 식별자의 첫 글자는 비숫자 문자를 사용할 수 있다. 비숫자 문자에는 영문 대소문자, 밑줄 문자
    ('_')가 포함되며, C++11부터 다국어 문자도 포함된다.
  2. 첫 글자 이후의 문자는 비숫자 문자와 숫자를 사용할 수 있다.
  3. 표준에는 식별자 길이에 제한은 없다. 그러나 구현에 따라 식별에 사용되는 문자의 수가 정해지기도 한다.
  4. 특수문자는 식별자에 포함될 수 없다. 다만 MS 비주얼 C++에서는 '$'를 사용할 수 있다.
  5. 키워드는 식별자로 사용이 불가하다.

auto 키워드

  • 변수를 선언할 때는 그 변수가 저장할 값의 타입(자료형)을 지정해야 한다.
  • C++11의 자료형 추론을 활용하면 변수를 초기화하는 값의 타입에 맞게 변수를 선언할 수 있다.
  • 자료형 추론은 auto 키워드를 사용한다.
auto i{10}; // int i{10}; 과 동일

const 키워드

  • 변수의 값이 항상 고정된 값을 갖게 하려면 변수를 선언할 때 const라는 키워드를 사용한다.
const double PI{3.14159};
  • const 키워드로 지정된 저장 영역은 값을 수정할 수 없다.
  • #define 전처리 지시어를 사용하여 매크로 상수를 정의하는 것도 하나의 방법이 될 수 있지만 매크로 상수를 사용하는 것은 해당 값의 리터럴을 직접 사용하는 것과 같으므로 이에 대한 참조나 포인터를 사용할 수 없다. 따라서 C++에서는 const 키워드를 사용하는 것이 일반적이다.

constexpr 키워드

  • constexpr 키워드는 그 값을 컴파일할 때 평가하라는 의미이다.
  • 이것은 실행 중(런타임)에 값을 평가하는 것에 비해 효율적으로 동작할 수 있다.
int a;
std::cin >> a;
const int b = 20;
const int c = a;

constexpr int D1 = a + 10;  // ERROR: 컴파일 타임에 a값이 확정되지 않아 알 수 없다
constexpr int D2 = b + 100; // OK: 컴파일 타임에 b값을 알 수 있기 때문에 가능
constexpr int D3 = c*3;     // ERROR: 컴파일 타임에 a값이 확정되지 않아 c값도 알 수 X
  • 함수도 constexpr 키워드로 선언(구현)할 수 있다. 모든 인수가 constexpr 특징을 갖는 경우 컴파일 타임에 값을 구할 수 있게 하라는 의미이다.
constexpr fac(int n) { return n > 1 ? n*f(n-1) : 1; }

void f(int x){
    constexpr int a = fac(4); // 컴파일 타임 때 결정될 수 있기 때문에 컴파일 시에 계산 O
    int b = fac(x)  // 인수가 constexpr하지 않다 => 실행 중 계산
}

타입과 변수 정의

기본 타입

  • 기본 자료형이란 프로그래밍 언어에서 데이터를 표현하기 위한 기본적인 표현 형식이다.
  • C++의 기본 자료형은 고정소수점 표현 방식인 정수형 자료형과 부동소수점 표현 방식인 실수형 자료형으로 분류할 수 있다.
  • C++ 기본 타입

고정된 너비 정수

  • C++은 정수 변수가 특정 최소 크기를 가질 것을 보장하고, 시스템에 따라서 크기가 더 커질 수 있다.
  • 정수 변수의 크기가 고정되어 있지 않은 이유: C는 컴파일러가 대상 컴퓨터 아키텍처에서 가장 잘 작동하는 int의 크기를 선택할 수 있도록 의도적으로 정수의 크기를 열어 두는 것을 선택했다. 그것을 그대로 C++도 계승한 것인데, 대상 아키텍처에 따라 크기가 달라질 수 있는 변수를 다루는 것은 좀 터무니없는 일이다.
  • 크로스 플랫폼을 위해 C99에서는 모든 아키텍처에서 같은 크기를 갖도록 보장하는 고정 너비 정수(stdint.h)를 정의했다.

#include <iostream>
#include <cstdint>

int main()
{
    std::int16_t i(5); // direct initialization
    std::cout << i;
    return 0;
}
  • int8_t와 uint8_t는 각각 char와 unsigned char와 같이 동작할 수 있지만 시스템에 따라 다르게 동작할 수도 있다 => int8_t와 uint8_t 사용은 자제
#include <iostream>
#include <cstdint>

int main(){
    std::int_8t myInt = 65; 
    std::cout << myInt // 보통 'A'를 출력하겠지만 시스템에 따라 65가 출력될 수도 있다.

    return 0;
}

부동 소수점 숫자

  • C++에는 float, doublelong double 과 같은 부동 소수점 자료형이 있다.
  • 정수와 마찬가지로 C++은 이러한 자료형의 크기를 지정하지 정의하지 않았다.
  • 현대 아키텍처에서 부동 소수점 표현은 거의 항상 IEEE 754 바이너리 형식을 따른다. 이 형식에서 float은 4 byte이고 double은 8 byte이며 long double은 8, 12, 16 byte 중 하나다.

  • 정밀도와 범위
    • 부동 소수점 숫자는 특정 유효 자릿수까지만 저장하고 나머지는 손실된다.
    • 부동 소수점의 정밀도(precision)는 정보 손실 없이 나타낼 수 있는 유의한 자릿수를 정의한다.
    • 부동 소수점 숫자를 출력할 때 std::cout의 기본 정밀도는 6이다. 즉, 모든 부동 소수점 숫자는 6자리까지만 유의하다고 가정하여 이후는 잘라낸다.
#include <iostream>

int main()
{
    float f;
    f = 9.87654321f; // f suffix means this number should be treated as a float
    std::cout << f << std::endl;
    f = 987.654321f;
    std::cout << f << std::endl;
    f = 987654.321f;
    std::cout << f << std::endl;
    f = 9876543.21f;
    std::cout << f << std::endl;
    f = 0.0000987654321f;
    std::cout << f << std::endl;
    return 0;
}

output:
9.87654
987.654
987654
9.87654e+006
9.87654e-005
  • 그러나 <iomanip> 헤더 파일에 정의된 std:setprecision() 함수를 사용해서 cout에서 출력되는 기본 정밀도를 재정의(override)할 수 있다.
#include <iostream>
#include <iomanip> // for std::setprecision()

int main()
{
    std::cout << std::setprecision(16); // show 16 digits
    float f = 3.33333333333333333333333333333333333333f;
    std::cout << f << std::endl;
    double d = 3.3333333333333333333333333333333333333;
    std::cout << d << std::endl;
    return 0;
}

output:
3.333333253860474
3.333333333333334
  • 정밀도를 16자리로 설정했으므로 위의 각 숫자는 16자리로 출력된다. 하지만 그 숫자들이 16자리까지 모두 정확하지는 않다.
  • 부동 소수점 자료형의 변수가 가지는 정밀도는 자릿수 크기('floatdouble보다 작다.)와 저장되는 특정 값에 따라 달라진다.
  • float 값의 정밀도는 6 ~ 9 자리다.
  • double 값의 정밀도는 15 ~ 18 자리로 16
  • long double 은 얼마나 많은 바이트를 차지하느냐에 따라 최소 15, 18 또는 33의 자리 정밀도를 가진다.
  • 정밀도 문제는 단지 소수점 이하의 숫자에만 영향을 미치는 것이 아니라, 너무 많은 유효 숫자에도 영향을 미친다. 큰 숫자를 고려해 보자.
#include <iostream>
#include <iomanip> // for std::setprecision()

int main()
{
    float f(123456789.0f); // f has 10 significant digits
    std::cout << std::setprecision(9); // to show 9 digits in f
    std::cout << f << std::endl;
    return 0;
}

Output:
123456792
  • 123456792_는 123456789_보다 크다. 값 123456789.0_은 10자리 숫자이지만 float은 일반적으로 7자리 정밀도를 가지기 때문에 정밀도를 잃고, 예상하지 못한 결과가 출력된다.
  • 따라서 변수가 보유 할 수가 있는 것보다 더 정밀도가 필요한 부동 소수점 숫자를 사용할 때는 주의해야 한다.

  • 무한 시퀀스로 표현되는 소수
    • binaray(2진수)에서 0.1은 0.00011001100110011…와 같은 무한 시퀀스로 표현된다. 이 때문에 부동 소수점 숫자에 0.1을 지정하면 0.1은 두자리 실수임에도 정밀도 문제가 발생한다.
#include <iostream>
#include <iomanip> // for std::setprecision()

int main()
{
    double d(0.1);
    std::cout << d << std::endl; // use default cout precision of 6
    std::setprecision(17);
    std::cout << d << std::endl;
    return 0;
}

This outputs:
0.1
0.10000000000000001 (: rounding error)
  • 정밀도를 17로 설정했다. 메모리 제한으로 인해 무한 시퀀스에서 근사치를 잘라내야 했기 때문에 반올림이 발생하고 그 결과 수치는 정확하게 0.1이 아니다. 이를 반올림 오류(rounding error)라고 한다.
  • 반올림 오류(rounding error)는 예상치 못한 결과를 초래하기도 한다.
#include <iostream>
#include <iomanip> // for std::setprecision()

int main()
{
    std::cout << std::setprecision(17);

    double d1(1.0);
    std::cout << d1 << std::endl;

    double d2(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1); // should equal 1.0
    std::cout << d2 << std::endl;
}

This outputs:
1
0.99999999999999989 (: rounding error)

변수 정의의 기본

  • 한 줄에 여러 변수 정의
int a, b;
  • 한 줄에 여러 변수 정의와 초기화
int a = 5, b = 6;
int c(7), d(8);
int e{9}, f{10};

변수 크기와 sizeof 연산자

  • 특정 시스템에서 자료형의 크기를 결정하기 위해 C ++은 sizeof라는 연산자를 제공한다.
  • sizeof 연산자는 자료형 또는 변수를 가지고 크기를 byte 단위로 반환하는 연산자다.
#include <iostream>

int main()
{
    std::cout << "bool:\t\t" << sizeof(bool) << " bytes" << std::endl;
    std::cout << "char:\t\t" << sizeof(char) << " bytes" << std::endl;
    std::cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << std::endl;
    std::cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << std::endl;
    std::cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << std::endl;
    std::cout << "short:\t\t" << sizeof(short) << " bytes" << std::endl;
    std::cout << "int:\t\t" << sizeof(int) << " bytes" << std::endl;
    std::cout << "long:\t\t" << sizeof(long) << " bytes" << std::endl;
    std::cout << "long long:\t" << sizeof(long long) << " bytes" << std::endl;
    std::cout << "float:\t\t" << sizeof(float) << " bytes" << std::endl;
    std::cout << "double:\t\t" << sizeof(double) << " bytes" << std::endl;
    std::cout << "long double:\t" << sizeof(long double) << " bytes" << 
    std::endl;
    return 0;
}
bool:           1 bytes
char:           1 bytes
wchar_t:        2 bytes
char16_t:       2 bytes
char32_t:       4 bytes
short:          2 bytes
int:            4 bytes
long:           4 bytes
long long:      8 bytes
float:          4 bytes
double:         8 bytes
long double:    8 bytes

변수 초기화

  • C++은 변수를 초기화하는 세 가지 기본 방법을 지원한다
      1. 대입 연산자( = )를 사용한 **복사 초기화**
      int value = 5;
      1. 괄호()를 사용한 **직접 초기화**
        • 직접 초기화는 일부 데이터 타입에서 복사 초기화보다 성능이 뛰어날 수 있다.
        int value(5);
      1. 중괄호({})를 사용한 **유니폼 초기화**
        • 복사 초기화 및 직접 초기화는 일부 타입의 변수에 대해서만 작동하는데 모든 데이터 타입에서 작동하는 단일 초기화 메커니즘을 위해 C+11은 유니폼 초기화라는 새로운 형태의 초기화 메커니즘을 추가했다.
        int value{5};
        • 빈 중괄호({})로 초기화하면 기본 초기화가 되고 기본 초기화는 변수를 0으로 초기화
        int value{};
        • 유니폼 초기화는 형 변환을 허용하지 않는다.
        int value{4.5} // error: an integer variable can not hold a non-integer 

'Language > C++' 카테고리의 다른 글

[Basic] 상수와 리터럴  (0) 2025.07.07
[OOP] 생성자와 소멸자  (1) 2025.06.08
[OOP] 클래스 선언과 객체 정의  (0) 2025.06.06
[Basic] 배열, 포인터, 참조  (0) 2025.06.05
[Basic] C++ 프로그램 작성 및 빌드  (0) 2025.06.05