C Primer Plus - 3장: C의 기본 데이터형(1)

요나·2023년 5월 16일
0

C Primer Plus

목록 보기
3/3

본 포스팅은 Stephen Prata의 C Primer Plus를 읽고 요약한 글입니다.


#include <stdio.h>

int main(void)
{
  float weight, value;

  printf("몸무게 입력하시오. (파운드 단위)\n");
  scanf("%f", &weight);

  value = 1700.0 * weight * 14.5833;

  printf("플래티넘 가치로 환산한 당신의 몸값: %.2f\n", value);

  return 0;
}

변수와 상수

  • 프로그램은 정보를 담고 있는 수와 문자들, data를 가지고 작업한다.
  • 프로그램이 실행되는 동안 값이 변하지 않고 유지될 때 constant라고 부르며, 프로그램이 실행되는 동안 값이 변하거나 대입될 수 있으면 variable이라고 부른다.

데이터 타입 키워드

  • 상수는 표시된 문자 그대로의 데이터형으로 인식되지만, 변수는 선언문을 통해 타입을 알려줘야 한다.
K&R C 키워드C90 K&R 키워드C99 키워드
int, long, short, unsigned, char, float, doublesigned, void_Bool, _Complex, _Imaginary
  • int는 C의 정수 기본형이고, long, short, unsigned, signed는 기본형 정수의 변형이다.
  • char은 문자에 사용되며, 작은 정수들을 나타내는 데 사용할 수도 있다.
  • float, double은 소수점이 있는 수를 나타낸다.
  • _Booltruefalse를, _Complex_Imaginary는 복소수와 허수를 나타낸다.
  • C의 데이터 타입은 컴퓨터에 어떻게 저장되느냐에 따라 Integer 범주와 Floating-point 범주로 분류된다.

비트, 바이트, 워드

  • bit, byte, word는 1) 데이터 단위 표시 2) 메모리 단위 표시에 사용된다.
    • bit: 메모리의 가장 작은 단위. 0(off) 또는 1(on) 중 하나만 저장할 수 있다.
    • byte: 메모리의 통상적 단위. 거의 모든 컴퓨터에서 1바이트 당 8비트의 표준 정의를 따른다. (하지만 C는 좀 다르다.) 8비트 구성된 1바이트는 256(=2⁸)개의 비트 패턴을 나타내며, 이 패턴들은 2진 코드를 사용하여 표현한다.
    • word: 특정 컴퓨터를 설계할 때 (프로세서 단위에 따라) 정해지는 고유 메모리 단위. 8비트, 16비트, 32비트, 64비트 워드까지 있다. 더 큰 크기의 워드는 데이터를 더 빠르게, 옮길 수 있고 더 많은 메모리에 접근할 수 있음을 의미한다.

정수형과 부동소수점형

  • 정수
    • integer는 소수부가 없는 수이며, 2진수 형태로 저장된다.
    • 예를 들어 8비트 워드에 7을 저장하면, 앞의 5비트가 0으로 설정되고 마지막 3비트가 1로 설정된다.
  • 부동소수점수
    • 부동소수점수는 실수(real number)와 비슷하다. 어떤 값에 소수점을 붙이면 부동소수점 값이 된다.
    • 부동소수점수 뒤에 E(exponent: 지수)을 표기하면 10ⁿ을 곱한다. (3.14E7 == 3.14 * 10ⁿ)
    • 부동소수점수를 저장하는 방법은 정수와 다르다. float-point number는 하나의 수를 소수부분(fractional part)와 지수부분(exponenet part)로 나누어 따로 저장한다.
    • 예를 들어 7.00.7E1로 바꿀 수 있는데, 0.7이 소수부분 이며 E1이 지수부분이다.
    • 지수부분이 메모리에 저장될 때는 10의 지수가 아닌 2의 지수를 2진수로 저장한다.
    • 한 범위에 들어 있는 실수의 수는 무한하기 때문에, 대부분의 부동소수점 값은 실제 값의 근사값이다.
    • 일반적으로 부동소수점수 연산은 정수 연산보다 느리다.

int

  • C는 다양한 정수형을 제공하는데, 특히 제공하는 값의 범위와 음수의 사용 여부에 따라 달라진다.
  • int는 signed integer이며, 양수, 음수, 0이 될 수 있다.
  • 허용되는 값의 범위는 컴퓨터 시스템에 따라 다른데, 일반적으로 int는 해당 컴퓨터의 1워드로 저장된다.
    • ISO C는 int 형의 최소 범위를 32bit(-32767~32767)로 규정한다.
    • 일반적으로 컴퓨터 시스템은 그 중 1비트의 값을 부호를 지정하는 데 사용한다.
  • 선언은 변수를 저장하기 위한 주소를 할당하고, 변수명은 이름을 연결하고, 초기값은 그 안에 대입된다.
  • 1, 21 -39 등은 integer constant이며, integer literal이라고도 불린다. (매우 크면 long int )
  • **int 값의 출력**
    • int의 포맷 지정자(format sepcifier: 값을 출력하는 포맷(char)으로 변환)는 %d이다.
    • printf() 함수는 포맷 지정자 개수와 인자 리스트의 개수가 1:1로 대응해야하지만, 맞지 않아도 에러를 검출하지 않는다. 이는 printf()는 전달인자의 개수가 확정되지 않아 컴파일러가 에러를 체크하는 통상적인 방법을 자신에게 적용되지 못하게 막는 특이한 설계로 만들어졌기 때문이다.
  • 8진수와 16진수
    • 8과 16은 2의 거듭제곱이지만 10은 아니기 때문에, 8진수와 16진수는 컴퓨터와 관련된 값을 표현하는 데 더 편리하다. 예를 들어 16bit 컴퓨터에서 자주 등장하는 65536은 16진수로 10000(=16⁴)이다.
    • 16진수에서 하나의 숫자는 정확히 4bit(=2⁴)에 대응한다. (8진수는 3bit에 대응.) 예를 들어 16진수 3은 0011이고, 16진수 5는 0101이며, 따라서 16진수 35는 0011 0101이며, 16진수 53은 0101 0011이다. 이와 같은 대응성은 16진수와 2진수 체계 사이의 전환을 쉽게 만든다.
    • 한 숫자가 어느 진수인지 알기 위해 prefix를 사용한다. (8bit: 0 / 16bit: 0x)
    • 어느 진수의 수를 사용하든 컴퓨터는 내부에서 2진수 코드로 저장한다.
    • printf()의 포맷 지정자로 8진수 %o, 16진수 %x를 사용하면 10진수 int를 해당 진수의 수로 출력하며, prefix도 함께 출력할 때는 %#o, %#x를 사용한다.
#include <stdio.h>

int main(void)
{
  int x = 100;

  printf("decimal: %d, octal: %o, hexadecimal: %x\n", x, x, x);
  printf("decimal: %d, octal: %#o, hexadecimal: %#x\n", x, x, x);
  
  return 0;
}

short, long, unsigned

  • C는 기본 정수형을 변경할 수 있는 short, long, unsigned 세 개의 형용사 키워드를 제공한다.
    • short: int보다 더 적은 메모리를 사용할 수 있기 때문에 메모리 절약 가능하다. (signed)
    • long: int보다 더 큰 메모리를 사용할 수 있다. (signed)
    • long long: C99표준에 추가되었는데, long보다 더 큰 메모리를 사용하며, 최소 64bit 이상을 사용해야 한다. (signed)
    • unsigned: 음수가 아닌 값들만 가지는 변수에 사용하며, 저장할 수 있는 수의 범위가 변한다. 예를 들어 16bit unsigned int는 -32768~32767 대신 0~65335까지 범위를 사용한다. 또한 부호를 나타내던 비트가 또 하나의 2진수로 사용되어 더 큰 수를 표현한다. (포맷 지정자는 %u다.)
  • C90 표준은 unsigned long int(=unsigned long)와 unsigned short int(=unsigned short)를 수용하고, C99 표준은 unsigned long long int(=unsigned long long)을 추가했다.
  • 키워드 signed는 부호 있는 데이터 타입에 그 의미를 더욱 명시적으로 나타내야 할 때 사용한다.
  • 왜 여러 개의 정수형이 필요한가?
    • shortint보다 길지 않고, longint보다 짧지 않다는 것 보장한다.
    • 이것은 데이터 타입을 기계에 맞추기 위해서인데, 현재 long long은 64bit, long은 32bit, short는 16bit로 설정하고 int는 시스템 고유 워드 크기에 따라 16 또는 32bit로 설정하기 때문이다. 즉 실제적으로는 일부 데이터 타입이 겹치고 있다.
    • C 표준은 각 데이터형의 최소 허용 크기에 대한 가이드라인을 제시한다.
      shortint는 둘 다 -32,767~32,767 이며, 16bit 단위에 해당한다.
      long의 최소 범위는 -2,147,483,647~2,147,483,647 이며, 32bit 단위에 해당한다.
      unsigned shortunsigend int의 최소 범위는 0~65,535 이며,
      unsinged long의 최소 범위는 0~4,294,967,295 이다.
      long long의 최소 범위는 -9,223,372,036,854,775,807~9,223,372,036,854,775,807 이며,
      unsigned long long의 최소 범위는 0~18,446,744,073,709,551,615 이다. (1884경)
    • unsigned는 음수가 없고 더 큰 범위의 양수를 가지므로 수를 카운트 할 때 사용한다.
    • longint로는 다룰 수 없는 큰 수를 다룰 때 사용한다. 하지만 longint보다 큰 시스템에서 long을 사용하면 계산이 느려지므로 반드시 필요한 경우 외에는 long을 사용하지 않는다.
    • intlong이 크기가 같을 경우, 32bit 정수를 사용해야하는데 16bit 프로세서에서도 동작하기를 원한다면 int 대신 long을 사용해야 한다. (16bit에서는 int가 16bit이므로.) 마찬가지 이유로 64bit 정수가 필요할 때는 long long을 사용해야 한다.
    • int가 32bit인 시스템에서 16bit 값을 필요할 때는 short를 사용하는 것이 중요하다. (특히 배열)
    • 또한 short를 사용하는 또 다른 이유는, 컴퓨터의 특정 구성 요소들에 의해 사용되는 하드웨어 레지스터들과 크기가 일치할 수 있기 때문이다.
  • 정수 오버플로우
    • 어떤 정수형 변수에 허용되는 최대값보다 더 큰 수를 대입하면 오버플로우가 발생한다.
    • 오버플로우 시, unsigned int는 0, signed int는 (일반적으로) 음의 최소값부터 다시 시작한다.
    • 측 최댓값을 초과했을 경우 컴파일러가 잡아내지 못하기 때문에, 방지하는 장치를 마련해야 한다.
    • C 표준은 unsigned int에 대한 규칙만 강제하기 때문에 signed int는 결과가 다를 수도 있다.
  • long 상수와 long long 상수
    • int 보다 큰 수를 사용하는 경우, 컴파일러는 long int로 가정하며 long 최대값도 초과할 경우, unsigned longlong longunsigned long 순으로 취급한다. (8진수, 16진수도 같다.)
    • 컴퓨터 메모리 주소를 명시적으로 사용할 필요가 있는 경우 컴파일러가 작은 수를 long int로 저장하기도 하며, 일부 C 표준 함수들이 long 값을 요구하기도 한다.
    • 작은 상수를 long 타입으로 취급할 때는 L을 suffix로 붙인다. (unsigned 일 때는 UL, LU)
    • 예를 들어 16bit int, 32bit long을 사용하는 시스템에서 7은 16bit, 7L은 32bit로 취급된다.
    • long long 타입을 지원하는 시스템에서는 LL을 suffix로 사용할 수 있다. (unsigned일 때는 ULL, LLU)
    • 포맷 지정자는 %ld인데, intlong이 같은 크기일 때는 %d로도 가능하지만 호환성에 안 좋다.
    • long 타입의 8진수, 16진수를 출력할 때는 %lo, %lx을 사용한다. (포맷 지정자는 소문자가 관례.)
    • short 타입은 %h, %ho, %hx를 사용한다.
  • unsigned int%u가 아닌 %d로 지정하면, signed int의 최댓값을 초과할 경우, 원하지 않는 값이 출력된다. (long, long long 값에 다른 지정자를 사용하면 컴파일 에러를 발생시킨다.)
  • 반면 short int%h이든 %d이든 동일한 값이 출력되는데, 이는 C가 short 값이 함수에 인자로 전달될 때, 컴퓨터가 가장 효율적으로 처리할 수 있는 정수 크기인 intshort 값을 변환하기 때문이다.
#include <stdio.h>

int main(void)
{
  unsigned int un = 3000000000;
  short end = 255;
  long big = 65337;
  long long verybig = 12345678912345;

  printf("un = %u and not %d\n", un, un); // 3000000000 1
  printf("end = %hd and %d\n", end, end); // 255 255
  // printf("big = %ld and not %hd\n", big, big);
	// 컴파일 에러 발생: long 변수에 short 지정자 불가능
  // printf("verybig = %lld and not %ld\n", verybig, verybig);
	// 컴파일 에러 발생: long long 변수에 long 지정자 불가능

  return 0;
}

char

  • char는 문자들을 저장하는 데 사용되지만, 기술적으로 볼 때 char는 정수형인데, 실제로는 문자가 아닌 정수들을 저장하기 때문이다.
  • 컴퓨터는 이 정수들이 어떤 문자들을 나타내도록 약속되어 있는 코드값(ASCII, Unicode)을 사용한다.
    • ASCII: 0부터 127까지의 정수(7bit)에 해당하는 범위이며, char는 8bit이므로 전부 담을 수 있다.
    • Unicode: ISO/IEC 10646 표준을 준수하는 코드값으로, 약 42억(32bit)의 범위에 해당한다.
  • C 언어는 char이 사용하는 비트 수를 1byte로 정의한다.
  • 문자(char) 상수는 무조건 작은 따옴표(’’)를 사용해야하며, 큰 따옴표(“”)는 문자열로 인식한다.
  • char 변수에 int 상수를 대입하면 이에 해당하는 (시스템이 ASCII를 사용하는 경우) 문자를 넣는다.
  • C는 문자 상수를 char가 아닌 int로 취급하기 때문에 ‘FATE’와 같은 32bit(8bit*4) 문자 상수도 정의할 수 있다. 하지만 이것을 char 변수에 대입하면 마지막 8bit만 사용되기 때문에 ‘E’값만 얻게 된다.
    warning: implicit conversion from 'int' to 'char' changes value 
    from 1178686533 to 69 [-Wconstant-conversion]
    # 현재는 컴파일 에러가 발생하여 직접적으로 동작하지는 않지만,
    # 문자 상수가 'int'라는 것과 변환된 값이 69(='E')인 것을 볼 수 있다.
  • ASCII 코드 중 일부는 출력되지 않는 문자들은 ASCII 코드 값(정수), escape sequence로 표현한다.
    • 특히 \, , 를 문자열 안에서 사용할 때 이스케이프 시퀀스가 꼭 필요하다.
    • 어떤 문자를 8진수나 16진수 ASCII코드로 표현할 때는 ‘\007’, ‘\x010’ 식으로 표현한다.
    • ASCII 코드 보다는 이스케이프 시퀀스를 사용하는 것이 이식하기에 더 용이하다.
  • char 변수는 1byte 정수값으로 저장되기 때문에 %d로 지정하면 해당 ASCII 코드 값의 정수를 출력하며, 마찬가지로 int 변수 역시 %c로 지정하면 해당 ASCII 코드 문자를 출력할 수 있다.
#include <stdio.h>

int main(void)
{
  char ch;
  int num;

  printf("Enter any character: ");
  scanf("%c", &ch); // E
  printf("ASCII code value of charcter %c is %d.\n", ch, ch); // E 65
  printf("Enter any number less then 255: ");
  scanf("%d", &num); // 65
  printf("ASCII code of number %d is %c.\n", num, num); // 65 E
  

  return 0;
}
  • C 컴파일러의 종류에 따라 charsigned(-128~127)일 수도, unsigned(0~255)일 수도 있다.
    • C90부터 컴파일러의 디폴트 char이 무엇이든 상관없이, 키워드를 붙여 signed char, unsigned char를 사용하도록 허용하고 있으며, 이는 작은 정수를 다룰 때 유용하다.
    • 일반적으로 문자를 다룰 때는 디폴트 char를 쓰는 것이 낫다.

_Bool

  • C99에 추가된 타입으로, truefalse를 나타낸다.
  • C에서는 1true를, 0으로 false를 나타내기 때문에, 실제로는 _Bool 역시 int 타입이다.
profile
var yona = IOSDeveloper(stage: 0)

0개의 댓글