타입

Updated:

타입

저번 Chapter 1. C 언어 시작에서는 C 언어의 배경부터 C 언어만의 특징, 표준 입출력 함수에 대해서 알아보았다. 이번에는 C 언어에서 사용되는 여러 타입에 대해서 정리하고자 한다.
이 글은 Chapter 2. 타입을 정리한 내용이다.
참고한 사이트 : TCLSchool

변수

변수(variable)란 데이터(data)를 저장하기 위해 이름을 할당받은 메모리 공간을 의미한다.
C 언어에서 숫자에 관련된 변수는 정수형, 실수형 변수로 구분할 수 있고, 정수형은 다시 char형, int형, long형 변수로, 실수형은 float형, double형 변수로 나눌 수 있다.
이외에도 메모리 주소를 관리하는 포인터 변수, 정보를 묶는 구조체 변수도 있다.

  1. 변수의 이름 생성 규칙
    변수의 이름을 정하는 데에 문법적인 제약은 없다. 하지만 변수의 이름은 사용자 혹은 다른 사용자가 알아보기 쉽도록 의미있게 지어야 한다. 변수의 이름을 짓는 규칙은 다음과 같다.

    • 변수의 이름은 영문자(대소문자), 숫자, 언더스코어(_)로만 구성된다.

    • 변수의 이름은 숫자로 시작될 수 없다.

    • 변수의 이름 사이에는 공백을 포함할 수 없다.

    • 변수의 이름으로 키워드를 사용할 수 없다.

      • C 언어에서 사용되는 키워드(keyword)

        auto beak case char const continue default
        do bouble else enum extern float for
        goto if int long register return short
        signed sizeof static struct switch typedef union
        unsigned void volatile while      
  2. 비트(bit)와 바이트(byte)
    컴퓨터는 2진수를 사용하여 작업한다.
    비트란 데이터를 처리하기 위한 데이터의 최소 단위이다. 이러한 비트에는 0과 1 중 하나만 저장할 수 있다. 바이트란 비트가 8개가 모여 구성되며, 한 문자를 표현할 수 있는 최소 단위이다.

  3. 변수와 메모리 구조
    변수는 메모리 주소(address)를 기억하는 역할을 한다. 메모리 주소란 메모리 공간을 서로 구분하기 위해 사용되는 식별자로, 다시 말해 고유 주소이다.
    변수를 참조할 때, 데이터를 참조하기 때문에 변수는 메모리 주소와 데이터의 형태, 길이도 기억해야 한다.

    variable_data
    변수와 데이터, TCL

  4. 변수의 선언
    메모리 공간을 할당받는 행위를 변수의 선언이라고 한다. 선언되지 않은 변수를 사용하려고 하면, C 컴파일러는 오류를 발생시킨다. 변수를 선언할 때에는 선언 혹은 선언과 초기화를 하는 방법이 있다.

    • 선언만 하는 방법

      int num; // 변수 선언 -> 쓰레깃값이 들어있음
      ...
      num = 20; // 변수 초기화 (초기값: 20)
      

      변수 선언에서 타입과 데이터가 일치해야 변형이 일어나지 않는다. 또한 초기화를 하지 않으면 변수에는 사용자가 원하는 값이 아닌 쓰레깃값이 들어있다. 즉 초기화가 매우 중요하다.

    • 선언과 초기화를 동시에 하는 방법

      double num = 1.23; // 선언과 동시에 초기화
      

      double형으로 선언함과 동시에 1.23이라는 값을 초깃값으로 초기화해주는 방식이다.

    • 동시에 여러 변수를 선언

      int num01, num02; // int형 동시 선언
      double num03 = 1.23, num04 = 4.56; // double형 동시 선언, 초기화
      

      타입만 동일하다면 위처럼 여러 변수를 동시에 선언할 수도 있다.

상수

상수(constant)란 변수와 마찬가지로 데이터를 저장할 수 있는 메모리 공간이다. 하지만 변수와는 다르게 상수는 프로그램이 실행되는 동안 저장된 데이터를 변경할 수 없다.
상수는 표현 방식에 따라 둘로 나누어 진다.

  1. 리터럴 상수(literal constant)
    변수와 달리 메모리 공간을 가리키는 이름을 가지고 있지 않다. 정수형, 실수형, 문자형 등으로 구분된다. 정수형은 123, -456 같이 아라비아 숫자와 부호로 표현되고, 실수형은 3.14, -45.6와 같이 소수로 표현된다. 문자형은 ‘a’, ‘Z’와 같이 따옴표(‘‘)로 감싸진 문자로 표현된다.

  2. 심볼릭 상수(symbolic constant)
    심볼릭 상수는 변수와 마찬가지로 이름을 가지고 있는 상수이다. 심볼릭 상수는 선언과 동시에 초기화되어야 하며, const 키워드나 매크로를 이용하여 선언한다.

    const in MAX = 10; // const 키워드를 이용한 심볼릭 상수
    #define MAX 10; // #define 선행처리 지시자를 이용한 매크로 심볼릭 상수
    

    매크로를 이용한 정의 방법은 선행처리기에서 확인할 수 있다.

기본 타입

타입(data type)이란 데이터의 저장, 처리 방식을 표시해주는 이름표이다. C 언어에서는 여러 형태의 타입을 제공하는데 이를 기본 타입이라고 한다. 기본 타입은 크게 정수형, 실수형, 문자형으로 나눌 수 있다.

  1. 정수형 타입
    unsigned 키워드를 추가하면, 부호를 나타내는 최상위 비트(MSB, Most Significant Bit)까지도 수를 표현하는 데에 사용한다. 즉, 8개의 비트 모두를 사용하여 숫자를 표현하기에 음수를 표현할 수는 없지만 signed보다 2배 많은 정수를 표현할 수 있다.
    signed는 최상위 비트를 부호 표시에 사용한다. 또한, 생략이 가능하다.

    정수형 타입 할당되는 메모리의 크기 데이터 표현 범위
    (signed) short 2 바이트 - 32,768 ~ 32,767
    unsigned short 2 바이트 - 0 ~ 65,535
    (signed) int 4 바이트 - 2,147,483,648 ~ 2,147,483,647
    unsigned int 4 바이트 - 0 ~ 4,294,967,296
    (signed) long 4 바이트 - 2,147,483,648 ~ 2,147,483,647
    unsigned long 4 바이트 - 0 ~ 4,294,967,296

    정수형 타입 사용 시 데이터의 최대 크기가 타입에 맞는지 주의해야 한다. 해당 범위를 벗어나면 오버플로우(overflow)가 발생해 의도한 값과는 다른 값이 저장된다. 언더플로우(underflow)도 마찬가지이다.

    int num = 2147483647; // int형 타입이 저장할 수 있는 최댓값인 2^31 - 1
    printf("변수 num에 저장된 값은 %d입니다.\n", num);    
    num = 2147483648;     // int형 타입이 저장할 수 없는 숫자인 2^31
    printf("변수 num에 저장된 값은 %d입니다.\n", num);
       
    // 실행 결과
    변수 num 저장된 값은 2147483647입니다.
    변수 num 저장된 값은 -2147483648입니다. // 음수로 저장됨 (의도와 다른 값)
    

    컴퓨터는 int형 데이터를 가장 빠르게 처리한다. int형은 운영 체제 환경에 따라 달라질 수 있다. 16bit라면 2바이트, 32bit라면 4바이트 64bit라면 4바이트를 사용한다.
    또한 long형 데이터도 UNIX 64bit 환경에서는 8바이트를 사용하지만, window 64bit 환경에서는 4바이트를 사용한다. 이 부분은 추후 정리하여 글을 작성하겠다.

  2. 실수형 타입
    실수형은 정수형보다 넓은 표현 범위를 가진다. 다만 컴퓨터에서는 실수를 표현하는 방식은 반드시 오차가 발생한다.

    실수형 타입 할당되는 메모리의 크기 데이터의 표현 범위
    float 4 바이트 (3.4 X 10^-38) ~ (3.4 X 10^38)
    double 8 바이트 (1.7 X 10^-308) ~ (1.7 X 10^308)
    long double double형과 동일함. double형과 동일함.
    float num01 = 3.1415926535897932;  // float 타입의 상대 오차 10^-7
    printf("변수 pi에 저장된 값은 %.20f입니다.\n", num01);    
       
    double num02 = 3.1415926535897932; // double 타입의 상대 오차 10^-15
    printf("변수 pi에 저장된 값은 %.20f입니다.\n", num02);
       
    // 실행 결과
    변수 num01 저장된 값은 3.14159274101257324219입니다. // 소수점 7자리 부터 오류
    변수 num02 저장된 값은 3.14159265358979311600입니다. // 소수점 16자리 부터 오류
    
    실수형 타입 지수의 길이 가수의 길이 유효 자릿수
    float 8 비트 23 비트 소수 부분 6자리까지 오차없이 표현한다.
    double 11 비트 52 비트 소수 부분 15자리까지 오차없이 표현한다.

    실수의 표현에 대한 자세한 내용은 실수의 표현에서 다룬다.

  3. 문자형 타입
    문자 하나를 표현할 수 있는 타입이다. 컴퓨터는 2진수를 이용하기 때문에 문자도 숫자로 표현해야 할 필요가 있다. 그렇기에 어떤 문자를 어떤 숫자로 표현할 지 규칙이 필요하다.
    이러한 규칙 중 가장 많이 사용되는 것이 아스키코드(ASCII)이다. 아스키코드는 영문 대소문자를 사용하는 7비트의 문자 인코딩 방식으로 2^7 = 128개의 문자를 표현할 수 있다. (2진수)
    아스키코드의 구성은 다음과 같다.

    • 출력할 수 없는 33개의 문자
    • 출력할 수 있는 52개의 영문 대소문자, 10개의 숫자, 32개의 특수 문자1개의 공백 문자

    아스키코드에 대한 정보, 아스키코드, 위키피디아

    문자형 타입 할당되는 메모리 크기 데이터의 표현 범위
    (signed) char 1 바이트 2^7 ~ 2^-7
    unsigned char 2 바이트 0 ~ 2^-8
    char ch = 'a';
    printf("변수 ch에 저장된 값은 %c입니다.\n", ch);
    printf("변수 ch에 저장된 값은 %d입니다.\n", ch);
       
    // 실행 결과
    변수 ch 저장된 값은 a입니다. // 문자 a
    변수 ch 저장된 값은 97입니다. // 아스키 코드 번호 97번
    

    C 내부적으로 아스키코드에 해당하는 숫자를 저장하기 때문에 ‘a’를 정수형 서식 지정자를 통해 출력하면 97이라는 정수가 나오게 된다.

타입 변환

다른 타입끼리의 연산이 필요한 경우 같은 타입으로 만들어 줄 필요가 있는데, 하나의 타입을 다른 타입으로 바꾸는 것을 타입 변환(type conversion)이라고 한다.
표현 범위가 좁은 타입에서 넓은 타입으로 변환하는 것은 문제가 발생하지 않지만, 반대의 경우는 데이터의 손실이 발생한다. 즉, 범위를 고려하여 타입 변환을 해야한다.
타입 변환에는 크게 두 가지 방식으로 나눌 수 있다.

  1. 묵시적 타입 변환(자동 타입 변환, implicit type conversion) 대입 연산이나 산술 연산에서 C 컴파일러가 자동으로 실행해주는 타입 변환이다.
    대입 연산에서는 연산자의 오른쪽에 존재하는 데이터의 타입이 연산자의 왼쪽에 존재하는 데이터의 타입으로 변환된다.
    산술 연산에서는 데이터의 손실이 최소화되는 방향으로 변환된다.

    • 대입 연산에서 묵시적 타입 변환
    char ch = 200;
    int num01 = 3.14;
    double num02 = 5;
       
    printf("ch에 저장된 값은 %d입니다.\n", ch);
    printf("num01에 저장된 값은 %d입니다.\n", num01);
    printf("num02에 저장된 값은 %d입니다.\n", num02);
       
    // 실행 결과
    ch 저장된 값은 -56입니다. // 범위를 넘어 데이터 손실
    ch 저장된 값은 3입니다. // 소수 부분이 자동 삭제 (데이터 손실)
    ch 저장된 값은 5.000000입니다. // 더 넓은 범위이기 때문에 데이터 손실 x
    
    • 산술 연산에서 묵시적 타입 변환
    double result01 = 5 +3.14; // int형이 double형으로 변환 (데이터 손실 최소화)
    double result02 = 5.0f + 3.14; //float형이 double형으로 변환
       
    printf("result01에 저장된 값은 %f입니다.\n", result01);
    printf("result02에 저장된 값은 %f입니다.\n", result02);
       
    // 실행 결과
    result01 저장된 값은 8.140000입니다.
    result02 저장된 값은 8.140000입니다.
    

    컴파일러가 자동으로 수행하는 묵시적 타입 변환은 언제나 데이터 손실이 최소화되는 방향으로 이루어진다. 따라서 다음과 같은 순서대로 변환을 수행한다.
    char → short → int → long → float → double → long double

  2. 명시적 타입 변환(강제 타입 변환, explicit type conversion)
    사용자가 타입 캐스트(type cast) 연산자를 사용하여 강제적으로 타입을 변환하는 것이다.
    변환하고자 하는 데이터 앞에 괄호()를 추가하고, 그 안에 변환할 타입을 적으면 된다. 이 괄호가 타입 캐스트이다.

    int num01 = 1;
    int num02 = 4;
       
    double result01 = num01 / num02;
    double result02 = (double)num01 / num02;
       
    printf("result01에 저장된 값은 %f입니다.\n", result01);
    printf("result02에 저장된 값은 %f입니다.\n", result02);
       
    // 실행 결과
    result01 저장된 값은 0.000000입니다. // int끼리 계산한 값: 0 -> double 변환
    result02 저장된 값은 0.250000입니다. // double과 int : 0.25 -> double 변환
    

추가 및 수정

  • 정리할 주제
    • int형과 long형에 대한 내용
    • 실수 표현 방식 및 오차
    • 아스키코드
  • 관련 문제 및 오류

Tags:

Categories:

Updated:

Leave a comment