쉽게 풀어쓴 C 언어 EXPRESS 개정 3판 챕터4. 변수의 자료형
변수와 상수
변수란 무엇인가?
변수(variable)
- 변할 수 있는 값을 나타내는 기호
- 컴퓨터 프로그램은 값을 저장하기 위해 변수를 사용한다
- 변수는 메인 메모리(main memory)에 만들어진다
- 프로그램 안에서 변수를 만들고 변수에 이름을 부여한 후에, 변수 이름을 사용하여서 메모리 공간을 사용하게 된다
- 한 번 값이 저장되었어도 언제든지 다시 다른 값으로 변경이 가능하다
변수가 왜 필요한가?
사용자에게서 받는 데이터를 저장하는 장소이다
프로그램 코드에 직접 값을 넣는 것보다 변수를 사용하는 것이 프로그램을 유연하게 만든다
변수와 상수
상수(constant)
- 값이 한번 정해지면 변경할 필요가 없는 데이터들
- 상수도 메모리에 저장된다
- 메모리에 저장되기 때문에 자료형이 있다
- 상수에는 두 가지 종류가 있는데 하나는 이름이 붙지 않는 리터럴 상수(literal constant), 이름이 붙일 수 있는 기호 상수(symbolic constant)
자료형
자료형의 개념
데이터의 종류에 따라서 변수의 종류를 다르게 하는 것이 효율적이다
적절한 자료형을 사용해야만 메모리를 절약하면서 실행 속도를 빠르게 할 수 있다
데이터의 종류를 자료형(data type) 또는 데이터 타입이라고 한다
하나의 자료형으로 생성되면 해당되는 종류의 데이터만 저장할 수 있다.
자료형을 크게 나누면 정수형(integer type), 부동 소수점형(floating-point type), 문자형(character type)으로 나눌 수 있다.
정수형
- 정수 타입의 데이터를 저장할 수 있다
- short, int, long, long long 등이 정수형에 속한다.
부동 소수점형
- 실수 타입의 데이터를 저장할 수 있다.
- float, long, long double이 속한다
문자형
- 하나의 문자를 저장할 수 있다
- char형이 속한다
- 문자형은 정수형으로 분류하기도 한다. 왜냐하면 문자가 작은 정수로 표현되기 때문이다

자료형의 크기
sizeof
- 변수나 자료형의 크기를 바이트로 반환하는 연산자
정수형
정수형은 가장 기본적인 데이터 타입으로 정수를 저장할 수 있다
short(16비트)<=int(32비트)<=long(32비트)<=long long(64비트)
C에는 정수를 저장하는 비트의 개수에 따라 short, int, long으로 나누어진다.
컴퓨터에서의 정수는 할당되는 비트의 개수가 제한되기 때문에 한정된 범위만을 표현한다

정수형이 많은 이유는 용도에 따라 프로그래머가 선택하여 사용할 수 있게 하려는 것이다
비트수를 늘리면 정수의 범위는 확대시킬 수 있지만 메모리 공간을 더 많이 필요로 한다
다양한 크기를 가지는 정수 자료형을 제공하여 각자 필요에 따라서 적당한 자료형을 선택하여 사용하자는 취지이다.
정수의 범위는 int형이 몇 비트로 표현되느냐에 따라 달라진다.
int형 크기는 CPU가 메모리에서 한번에 읽어서 처리할 수 있는 비트의 크기(워드)와 관련이 있다
대부분의 컴퓨터에서는 32비트 CPU를 사용하고 따라서 현재 대부분의 컴퓨터에서 int형은 32비트이다
※ 각 자료형들의 크기는 CPU에 따라 달라지기 때문에 정확한 크기는 구현 세부사항이다
int형과 long형은 같은 범위를 가진다. VS에서도 long형은 int형과 같은 32비트로 표현된다
64비트 정수를 사용하려면 long long형을 사용하면 된다
unsigned, signed 수식자
unsigned, signed 키워드는 정수형 앞에 올 수 있다
unsigned
- 음수가 아닌 값만을 나타낸다
- int형에 비하여 표현할 수 있는 양수의 범위가 2배이다. 왜냐하면 부호를 나타냈던 최상위 비트가 값을 나타내는 비트로 사용되기 때문이다
- unsigned형 변수를 출력할 경우에는 형식 지정자 %u를 사용하여여 한다.
- %d를 사용하여도 값이 작을 때는 올바르게 출력하지만 값이 커지면 음수로 출력된다.
signed
- 반대로 음수도 가능하다
오버플로우
오버플로우(overflow)
- 변수가 나타낼 수 있는 범위를 넘는 숫자를 저장하려고 할 때 발생한다
- 정수형 변수를 이용하여 덧셈과 같은 산술 연산을 하는 경우, 산술 연산의 결과가 정수형이 나타낼 수 있는 범위를 넘어갈 때 발생한다.
- 오버플로우가 발생하더라도 컴파일러는 아무런 경고를 하지 않고 전체적으로 부정확한 결과가 계산된다
정수 상수
12나 100과 같이 숫자로 표기된다
기본적으로 int형으로 간주된다
만약 int형의 범위를 넘는 정수 상수는 컴파일러가 알아서 long형으로 취급한다. 만약에 안된다면 unsigned long형으로 변경될 수 있다
컴파일러는 상수값을 처리할 때, 가능한 자료형 중에서 가장 작은 자료형을 선택한다
만약 상수의 자료형을 프로그래머가 명시적으로 지저하고 싶은 경우는 123L처럼 정수 상수 뒤에 접미사 L을 붙이면 123이라는 상수를 long형으로 간주한다
만약 int형이 16비트인 시스템에서 그냥 12이라고 하면 16비트의 123이 되고 123L이라고 하면 32비트의 123L이 된다.

정수 상수는 10진법뿐만 아니라 8진법이나 16진법으로도 표기가 가능하다. 8진법으로 표기하려면 앞에 0을 붙이면 된다
16진법은 0부터 9까지의 숫자, A부터 F까지의 글자를 사용하여 나타낸다. 16진법으로 정수 상수를 표기하려면 앞에 0x를 붙이면 된다 16진법에서 알파벳의 대소문자는 구분하지 않는다
하드웨어 관련하여 비트 조작을 할 때는 10진법보다 16진법이 훨씬 사용하기 편리하다
기호 상수
기호상수(symbolic constant)
- 기호에 의하여 상수를 표현한 것이다
- 기호를 사용하면 프로그램을 읽기가 쉬워진다
- 상수 값을 변경하려고 하는 경우에, 쉽게 할 수 있다
- 기호 상수를 사용했다면 기호 상수의 정의만 변경하면 된다

기호 상수를 선언하는 방법
1. #define 문장 사용
#define EXCHANGE_RATE 1120
- #define이 들어가는 문장은 보통 컴파일러가 동작하기 전에 전처리기(preprocessor)가 처리한다
- 일반적으로 기호 상수 이름은 다른 이름과의 구별을 위하여 대문자로 만든다
- 이 문장은 세미콜론으로 끝나지 않는다
- 문장이 아니고 단순히 기호를 값으로 대체하라는 명령을 전처리기에 알려주는 역할만 하기 때문이다
- 전처리기 문장은 정식 문장이 아닌 것이다
2. const 키워드 사용
const int EXCHANGE_RATE = 1120;
- const를 변수 선언 앞에 붙이면 상수가 된다. 선언 시에 const가 붙여진 변수는 일단 초기화된 후에 그 값이 변경될 수 없다.
-이 문장은 변수 선언과 같이 세미콜론으로 끝남을 주의해야한다.
※#define vs const
- const 키워드를 사용하는 것이 좋다. 자료형도 지정할 수 있고 상수가 정의되는 범위를 변수와 같이 제한할 수 있기 때문이다
- 배열이나 구조체와 같은 복합 데이터 타입에서도 const는 사용할 수 있다
내부적인 정수 표현 방식
컴퓨터는 모든 것을 0과 1만을 사용하는 2진수로 표현한다
정수도 0과 1의 조합으로 나타낼 것이다.
2진수의 하나의 자리수를 비트(bit)라고 한다.
양의 정수의 경우, 10진수를 2진수로만 바꾸어 메모리에 저장하면 된다
보통은 최상위 비트를 부호 비트(sign bit)로 사용하면 된다
맨 첫번째 비트가 0이면 양의 정수, 1이면 음의 정수를 의미한다.
CPU 내부에서는 덧셈 회로를 이용하여서 뺄셈을 한다. CPU의 복잡도를 줄이기 위하여 덧셈 회로만을 가지고 있는 것이다.
그래서 뺄셈을 하기 위해서 2의 보수를 구해야한다.
2의 보수
- 2진수의 각 비트들을 반전시킨 후에 1을 더한다.

부동 소수점형
컴퓨터에서 실수를 나타내는 방법
실수를 표현하는 방법
1. 고정 소수점(fixed point) 방식
- 소수점의 위치를 고정시키고 정수부를 위하여 일정 비트를 할당하고 소수부를 위하여 일정 비트를 할당하는 방식이다
- 예를 들어 32비트를 사용하여 실수를 표현한다면 16비트는 소수점 이상을, 나머지 16비트는 소수점 이하를 표현하는 방식이다
- 상위 비트의 최고로 큰 수는 2^15-1이고 하위 16비트는 2^16-1을 나타낼 수 있다.
2. 부동 소수점(floating point) 방식
- 소수점의 위치가 떠서 움직인다는 뜻이다
- 과학과 공학에서 필요한 아주 큰 수를 표현할 수 없다는 것이다
- 소수점의 위치를 움직임으로써 한정된 비트로 정밀도를 보다 높게 표현할 수 있다


- 가수와 지수를 따로 따로 표현하게 되면 표현할 수 있는 실수의 범위가 대폭 늘어난다
- 가수 부분은 6자리의 10진수로 표현이 가능하고 지수는 10^-38 ~ 10^38까지 표현이 가능하다
- 이전의 고정 소수점 방식에 비하면 범위가 대폭 늘어난 것을 알 수 있다
- 하지만 고정 소수점에 비해 계산 속도가 느리다. 그래서 부동 소수점 장치가 CPU에, 혹은 별도로 포함되어 있는 경우가 많다
정밀도
- 유효 숫자(즉 오차가 없는 숫자)의 개수로 나타낸다.
- 정밀도는 소수점 몇째 자리까지 오차 없이 표현이 가능한가에 대해 문제이다
- 가수 부분에 몇 비트가 할당되느냐에 따라 결정된다
부동 소수점 자료형
float(32비트)<=double(64비트)<=long double(64비트)
float
- 가장 작고 빠른 표현 방시긍로 32비트로 표현된다
- 8비트를 지수에 할당하고 나머지 24비트가 가수에 할당한다
- 유효숫자 6자리까지 나타낼 수 있다
- float형이 나타낼 수 있는 지수의 범위는 대략 10^-38부터 10^38까지이다
double
- 64비트를 사용한다
- 64비트 중에서 11비트 정도를 지수에 할당하고 나머지 53비트 가수에 할당된다
- 대략 유효 숫자 16자리를 나타낼 수 있다
- double형이 나타낼 수 있는 지수의 범위는 대략 10^-308 ~ 10^308이다
- PC에서 long double형은 double과 같다

실수를 출력하는 형식 지정자
실수를 출력하는 기본적인 형식 지정자는 %f이다
실수 출력시 기본적으로 소수점 6자리까지만 출력된다
소수점 6자리를 넘으면 반올림된다
printf("%f", 0.123456789); // 0.123457 출력
실수에서 소수점 이하 자리수를 제한하려면 %10.3f와 같이 적어서 10자리 중에서 소수점 이하를 3자리로 만들 수 있다
실수를 1.2345e10과 같은 지수 표기법으로 출력하려면 %e나 %E를 사용한다.
%e와 %E는 지수 부분을 나타내는 문자가 소문자 e인지 대문자E인지만 다르다
%g는 실수를 출력할 때 만약 소수점 이하 6자리 안에서 표현이 가능하면 %f 방식을 사용하고 그렇지 않으면 %e를 사용하게 된다
printf("%e", 0.123456789); // 1.234568e-001 출력
※float같은 실수를 int같은 정수에 넣을 경우 컴파일러는 경고를 한다. 그리고 실수 중에서 소수점 이하는 없어지고 정수 부분만 정수변수에 대입한다.
#include <stdio.h>
int main(void)
{
float x=1.2345678901234567890;
double y=1.2345678901234567890;
printf("float의 크기 = %d\n", sizeof(float));
printf("double의 크기 = %d\n",sizeof(double));
printf("x = %30.25f\n",x);
printf("y = %30.25f\n",y);
}
/*
실행 결과
float의 크기=4
double의 크기=8
x = 1.2345678806304931640625000
y = 1.2345678901234566904321355
*/
float형의 변수인 x의 경우, 소수점 이하 8자리부터는 이상한 값이 출력되는 값을 알 수 있다. 이는 float형의 경우 유효 숫자가 대략 처음 6자리이기 때문이다
double형의 경우는 소수점 이하 16자리부터는 비교적 정확하게 표현됨을 알 수 있다. 유효 숫자의 제한이 있기 때문에 정수처럼 실수를 완벽하게 표현하지는 못한다는 점이다. 부동 소수점형은 오차가 있을 수 있다
부동 소수점 상수
부동 소수점 상수는 기본적으로는 소수점을 이용하여 표현된다
부동 소수점 상수는 기본적으로 double형으로 저장된다
만약 4바이트 크기의 float형 상수를 만들려면 실수 상수 끝에 f나 F를 붙여주면 된다
3.14592 // double형 상수(64비트)
3.14592F // float형 상수(32비트)
지수 부분은 E나 e를 사용하여 표시한다
실수 | 지수 표기법 | 의미 |
123.45 | 1.2345e2 | 1.2345*10^2 |
0.000023 | 2.3e-5 | 2.3*10^-5 |
상수 2.은 정수가 아니고 2.0의 부동 소수점 상수로 취급된다
프로그램에서는 정수 연산과 부동 소수점 연산을 구분한다. 만약 소수점이 없으면 정수 연산으로 취급한다
오버플로우와 언더플로우
오버플로우(overflow)
- 변수에 대입된 수가 너무 커서 변수가 저장할 수 없는 상황을 의미한다
- 컴파일러는 오버플로우가 발생되면 inf로 표시된다
- 부동 소수점의 경우, 오버플로우가 발생하면 컴파일러는 해당 변수에 무한대를 의미하는 특별한 값을 대입하고 printf()는 이 값을 INF라고 출력한다
언더플로우(underflow)
- 부동 소수점 수가 너무 작아서 표현하기가 힘든 상황이 언더플로우이다
- 컴파일러는 가수부를 조정하여 이것을 맞추려고 시도한다. 그래서 0.0123456*10^-38형태로 바꾸지만 가수부는 유효숫자 6자리정도만을 저장할 수 있으므로 실제로는 0.012345*10^-38만이 저장할 수 있다
- 가수부를 낮추는 것도 한도가 있기 때문에 너무 작으면 컴파일러가 0으로 만든다
정규 형태
- 부동 소수점에서 정규 형태는 가수부의 값이 1과 10사이의 수인 것을 말한다
부동 소수형을 사용할 경우 주의할 점
- 부동 소수점 연산은 정확하지가 않는 경우가 많다
- 적은 수의 비트를 가지고 넓은 범위의 실수를 표현하기 위한 방법이라 오차가 존재할 수 있다
- 부동 소수점 수를 사용할 때는 항상 오차가 발생할 수 있음을 염두에 두어야 한다
- 오차는 모든 컴퓨터에 공통적인 것이고 실수를 표현하는 비트가 많아질수록 이러한 오차는 작아진다
- 오차를 줄이려면 float보다는 double형을 사용하여야 한다
#include <stdio.h>
int main(void)
{
double x;
x = (1.0e20 + 5.0)-1.0e20; // 부동 소수점 연산에서는 오차가 발생한다. 5.0이 아니라 0으로 계산된다.
printf("%f\n",x);
return 0;
}
- 이러한 결과가 나오는 원인은 float형이나 double형 모두 이러한 정밀한 계산을 하기에 충분한 자리수가 확보하지 않았기 때문이다
- 숫자를 정확하게 기억시키려면 유효숫자가 적어도 20개는 있어야 한다.
- double형에서도 이 숫자는 정확하게 저장하기가 불가능하다
- 따라서 5.0이 들어갈 52비트 가수부가 표현할 수 없어서 버려지고 1.0e20으로 저장되는 것이다. 따라서 연산의 결과는 0.0이 된다
- 실수 x와 실수 y가 같은지를 비교할 때도 신경을 써야하고 x와 y가 정확하게 값이 일치하는 경우는 거의 없다.
문자형
문자와 아스키 코드
문자(character)
- 한글이나 영어에서의 하나의 글자, 숫자, 기호 등을 의미한다
- 거의 모든 정보가 문자를 통하여 전달되기 때문이다
아스키(ASCII : American Standard Code for Information Interchange)
- 영어의 알파벳에 기초를 둔 문자 엔코딩 방법이다
- 아스키 코드에는 인쇄가 불가능한 33개의 제어 문자 코드와 95개의 인쇄가 가능한 문자 코드가 있다
- 아스키 코드는 0에서 127까지의 숫자를 이용하여 문자를 표현한다. 따라서 7비트만 있어도 된다
- char형은 8비트이기 때문에 아스키 코드를 표현하고도 절반의 공간이 남는다
- 이 128부터 255까지는 확장 아스키 코드들이 차지한다. 즉, 그래픽 문자라든지 독일어에서만 사용되는 문자들이 정의되어있다
- 제어 문자 코드는 0부터 31까지를 차지한다. 아스키 코드에서 인쇄 가능한 코드는 스페이스 문자부터 시작한다

문자 변수와 문자 상수
문자가 정수로 표현되므로 정수를 저장할 수 있는 자료형은 문자도 저장할 수 있다
따라서 char형이 문자를 저장하는데 주로 사용된다
char형은 8비트 정수를 저장할 수 있다
int형도 문자를 저장하는데 사용된다
프로그램에서 문자를 저장하려면 다음과 같이 char형의 변수를 선언하면 된다
char code;
code = 65;
code = 'A';
// 두 개의 코드는 동일한 코드이다.
char형이 내부적으로는 정수를 저장하고 있다
문자 상수(character constant)
- 작은 따옴표로 감싸진 문자
- 컴파일러는 작은 따옴표로 감싸진 문자 상수를 만나면 이것을 아스키 코드로 변환한다
※ 한글 표현 방법으로는 첫 번째는 각 글자마다 하나의 코드를 부여하는 것이다. 한글에서 표현 가능한 글자의 개수는 11172개이고 16비트가 되어야 가능하다. 대표적인 코드 체계가 유니코드(unicode)이다. 유니코드(unicode)는 전세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 설계된 산업 표준이다. 또 다른 방법으로는 16비트를 사용하는 방법이지만 글자의 초성, 중성, 종성에 각각 5비트씩을 할당하고 가장 앞의 비트는 영숫자와 한글을 구분 짓는 기호로 하는 방법이다
제어 문자
아스키 코드표에는 제어 문자들도 함께 정의되어 있다. 제어 문자들은 인쇄될 수가 없고 주로 제어 목적으로 이용되는 문자들이다
제어 문자들은 출력할 수 없기 때문에 문자 상수로 만드는 것은 불가능하다
제어 문자들을 프로그램 안에서 표현하는 방법
1. 해당 아스키 코드 값을 직접 사용하는 것
2. 특수 문자열(escape sequence)을 이용해서 표현하는 방법
- 특수 문자열은 역슬래시(\)와 의미를 나타내는 한 글자를 붙여서 기술된다
- '\n' 문자가 특수 문자열이다. 이는 화면에서 다음줄의 시작 위치로 커서를 보내는 줄바꿈 문자이다.

특수 문자열은 큰따옴표 문자를 화면에 나타내는 데도 사용된다
원래 큰따옴표는 문자열(문자들이 모인 것)을 표시하는 역할을 한다
만약 큰따옴표를 화면에 나타내야할 경우가 있다면 특수 문자열이 사용된다.
특수한 기능을 가진 문자 앞에 역슬래시 \를 위치시키려면 문자의 특수한 의미가 사라지는 효과가 있다.
정수형으로서의 char형
char형은 사실은 8비트의 정수를 저장하는 자료형이다
문자뿐만 아니라 작은 정수값도 저장할 수 있다
char형은 시스템에 따라서 구현이 약간 달라질 수도 있다
char형은 부호가 있는 자료형으로 간주되지만 일부 시스템에서는 부호가 없는 자료형으로 구현된다
VS C++에서는 char형이 부호가 있는 자료형이다
char형에도 signed와 unsigned의 수식어를 사용할 수 있다
signed를 붙이면 확실하게 부호가 있는 자료형이 되고 unsigned를 붙이면 부호가 없는 자료형이 된다.
signed char형은 -128~127까지의 정수를 저장할 수 있고 unsigned char형은 0 ~ 255까지의 정수를 저장할 수 있다.
char형 변수에 아스키 코드를 대입한 상태에서도 정수처럼 여러 가지 연산을 할 수 있다
변수 code에 65가 들어 있는 상태에서 이것을 정수로 해석하여 사용할 수도 있고 아니면 이것을 아스키 코드로 간주하여 문자로 사용할 수도 있다
변수의 초기값
변수를 사용할 때 가장 주의할 점은 초기화되지 않은 값을 사용하는 것이다