본 포스팅은 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;
}
K&R C 키워드 | C90 K&R 키워드 | C99 키워드 |
---|---|---|
int, long, short, unsigned, char, float, double | signed, void | _Bool, _Complex, _Imaginary |
int
는 C의 정수 기본형이고, long
, short
, unsigned
, signed
는 기본형 정수의 변형이다.char
은 문자에 사용되며, 작은 정수들을 나타내는 데 사용할 수도 있다.float
, double
은 소수점이 있는 수를 나타낸다._Bool
은 true
와 false
를, _Complex
와 _Imaginary
는 복소수와 허수를 나타낸다.E
(exponent: 지수)을 표기하면 10ⁿ을 곱한다. (3.14E7 == 3.14 * 10ⁿ
)7.0
은 0.7E1
로 바꿀 수 있는데, 0.7
이 소수부분 이며 E1
이 지수부분이다.int
int
는 signed integer이며, 양수, 음수, 0이 될 수 있다.int
는 해당 컴퓨터의 1워드로 저장된다.int
형의 최소 범위를 32bit(-32767~32767)로 규정한다.1
, 21
-39
등은 integer constant이며, integer literal이라고도 불린다. (매우 크면 long int
)**int
값의 출력**int
의 포맷 지정자(format sepcifier: 값을 출력하는 포맷(char
)으로 변환)는 %d
이다.printf()
함수는 포맷 지정자 개수와 인자 리스트의 개수가 1:1로 대응해야하지만, 맞지 않아도 에러를 검출하지 않는다. 이는 printf()
는 전달인자의 개수가 확정되지 않아 컴파일러가 에러를 체크하는 통상적인 방법을 자신에게 적용되지 못하게 막는 특이한 설계로 만들어졌기 때문이다.0
/ 16bit: 0x
)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
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
다.)unsigned long int
(=unsigned long
)와 unsigned short int
(=unsigned short
)를 수용하고, C99 표준은 unsigned long long int
(=unsigned long long
)을 추가했다.signed
는 부호 있는 데이터 타입에 그 의미를 더욱 명시적으로 나타내야 할 때 사용한다.short
는 int
보다 길지 않고, long
은 int
보다 짧지 않다는 것만 보장한다.long long
은 64bit, long
은 32bit, short
는 16bit로 설정하고 int
는 시스템 고유 워드 크기에 따라 16 또는 32bit로 설정하기 때문이다. 즉 실제적으로는 일부 데이터 타입이 겹치고 있다.short
와 int
는 둘 다 -32,767~32,767 이며, 16bit 단위에 해당한다.long
의 최소 범위는 -2,147,483,647~2,147,483,647 이며, 32bit 단위에 해당한다.unsigned short
와 unsigend 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
는 음수가 없고 더 큰 범위의 양수를 가지므로 수를 카운트 할 때 사용한다.long
은 int
로는 다룰 수 없는 큰 수를 다룰 때 사용한다. 하지만 long
이 int
보다 큰 시스템에서 long
을 사용하면 계산이 느려지므로 반드시 필요한 경우 외에는 long
을 사용하지 않는다.int
와 long
이 크기가 같을 경우, 32bit 정수를 사용해야하는데 16bit 프로세서에서도 동작하기를 원한다면 int
대신 long
을 사용해야 한다. (16bit에서는 int
가 16bit이므로.) 마찬가지 이유로 64bit 정수가 필요할 때는 long long
을 사용해야 한다.int
가 32bit인 시스템에서 16bit 값을 필요할 때는 short
를 사용하는 것이 중요하다. (특히 배열)short
를 사용하는 또 다른 이유는, 컴퓨터의 특정 구성 요소들에 의해 사용되는 하드웨어 레지스터들과 크기가 일치할 수 있기 때문이다.unsigned int
는 0, signed int
는 (일반적으로) 음의 최소값부터 다시 시작한다.unsigned int
에 대한 규칙만 강제하기 때문에 signed int
는 결과가 다를 수도 있다.long
상수와 long long
상수int
보다 큰 수를 사용하는 경우, 컴파일러는 long int
로 가정하며 long
최대값도 초과할 경우, unsigned long
→ long long
→ unsigned long
순으로 취급한다. (8진수, 16진수도 같다.)long int
로 저장하기도 하며, 일부 C 표준 함수들이 long
값을 요구하기도 한다.long
타입으로 취급할 때는 L
을 suffix로 붙인다. (unsigned
일 때는 UL
, LU
)int
, 32bit long
을 사용하는 시스템에서 7
은 16bit, 7L
은 32bit로 취급된다.long long
타입을 지원하는 시스템에서는 LL
을 suffix로 사용할 수 있다. (unsigned
일 때는 ULL
, LLU
)%ld
인데, int
와 long
이 같은 크기일 때는 %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
값이 함수에 인자로 전달될 때, 컴퓨터가 가장 효율적으로 처리할 수 있는 정수 크기인 int
로 short
값을 변환하기 때문이다.#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)의 범위에 해당한다.char
) 상수는 무조건 작은 따옴표(’’
)를 사용해야하며, 큰 따옴표(“”
)는 문자열로 인식한다.char
변수에 int
상수를 대입하면 이에 해당하는 (시스템이 ASCII
를 사용하는 경우) 문자를 넣는다.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로 표현한다.\
, ‘
, “
를 문자열 안에서 사용할 때 이스케이프 시퀀스가 꼭 필요하다.‘\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;
}
char
이 signed
(-128~127)일 수도, unsigned
(0~255)일 수도 있다.char
이 무엇이든 상관없이, 키워드를 붙여 signed char
, unsigned char
를 사용하도록 허용하고 있으며, 이는 작은 정수를 다룰 때 유용하다.char
를 쓰는 것이 낫다._Bool
true
와 false
를 나타낸다.1
로 true
를, 0
으로 false
를 나타내기 때문에, 실제로는 _Bool
역시 int
타입이다.