C의 자료형과 변수에 대해 알아보고, 변수를 출력하는 방법에 대해 배웁니다.
2장에서 다룬 책장 그림을 생각해봅시다. 어떤 프로그래머가 BMI를 계산하는 프로그램을 만들려고 합니다. 그럼 프로그래머는 책장의 7번에는 사용자의 키를 저장하고, 9번에는 몸무게를 저장하는 등 책장의 어디에 무엇을 저장할지 결정하게 될 겁니다. 그런데 프로그램이 점점 더 커지고 복잡해지면 어떻게 될까요? 예를 들어, 이 프로그램을 회원이 100명인 헬스장에 판매하려고 합니다. 그럼 고려해야할 값이 적어도 200개 이상임을 알 수 있습니다. 아무리 머리가 좋다고 하더라도 200개의 숫자가 각각 무엇을 의미하는지 기억하는 것은 힘듭니다.
그래서 프로그래밍 언어에선 변수라는 개념을 도입했습니다. 수학에서의 변수는 변할 수 있는 값이지만, 프로그래밍 언어의 변수는 이름을 붙인 특정한 메모리의 공간입니다. 예를 들어, 7번 대신에 user_height라는 이름을 사용하고, 9번 대신에 user_weight라는 이름을 사용할 수 있습니다. 이렇게 메모리라는 개념을 추상화할 수 있습니다. 프로그래머가 코드를 통해 새 변수를 사용하고 싶다고 표현하면, 인터프리터나 컴파일러는 적절한 메모리 공간에 그 이름을 지어줍니다. 프로그래머가 몇 번 책장을 사용할지도 고려할 필요가 없습니다.
그런데 변수를 사용하기에 앞서, 자료형이라는 개념을 먼저 다룰 필요가 있습니다. 책장에 숫자가 들어가는 건 맞는데, 책장의 어떤 특정한 공간에 숫자를 무한정 넣을 수 있을까요? 어떤 공간에 2를 넣고, 어떤 공간에 8을 넣는다고 가정해봅시다. 근데, 두 공간을 차지하는 것 보다는 어떤 한 공간에 2000008을 넣는 게 더 낫지 않을까요? 숫자를 꺼낸 다음에 숫자를 백만으로 나눈 몫과 나머지를 사용하면 한 공간에 여러 숫자를 저장하는 것이 더 나아보입니다.
당연히 컴퓨터도 마법의 도구가 아니라 사람이 만든 기계인만큼, 한계를 가지고 있습니다. 2장에서 메모리는 0 또는 1을 저장할 수 있는 플립플롭이라는 회로 여러 개로 이루어진 장치라고 배웠습니다. 이렇게 0 또는 1을 저장할 수 있는 공간은 1 비트(bit)
의 크기를 가졌다고 합니다. 실제 메모리는 한 공간이 8비트의 크기를 가집니다. 즉, 한 공간에는 최대 8자리의 2진수만을 담을 수 있습니다. 1이 8개 있는 이진수 1111 1111(2)는 십진수로 표현하면 255입니다. 다시 말해 한 공간에는 0 부터 255 사이의 값만 담을 수 있습니다. 이 8비트를 1 바이트(byte)
라고 합니다.
테라바이트(terabyte)
나기가바이트(gigabyte)
에서 바이트라는 용어를 들어보셨을 것입니다.테라
나기가
는킬로(kilo)
나밀리(milli)
같은 SI 접두사들인데요, 자세히 알아보고 싶으신 분들은 여기를 참고해주세요. 참고로 과학에서와 다르게 컴퓨터에선 각 접두사가 1024배(2¹⁰)씩의 차이를 가집니다.
그럼 컴퓨터는 어떻게 큰 수를 다룰까요? 한 값을 저장할 때 여러 공간을 사용하면 됩니다. 공간을 하나만 쓰면 255까지밖에 저장하지 못하지만, 두 개 쓰면 65535(2¹⁶ - 1)까지, 네 개 쓰면 42억이 넘는 값도 저장할 수 있습니다.
자료형이라는 것은 이렇게 어떤 값을 저장할 때, 이 값이 메모리의 공간을 몇 칸 차지하고, 그 메모리에 저장된 걸 어떻게 해석할지 정해놓은 것입니다. 예를 들어, 어떤 자료형은 공간을 한 칸만 차지하는 정수이고, 어떤 자료형은 공간을 네 칸 차지하는 소수일 수 있습니다.
그럼 음의 정수는 어떻게 처리할까요? 여러가지 방법이 있겠지만, 현재 제작되는 컴퓨터에선 2의 보수
라는 개념을 사용합니다. 2의 보수의 자세한 원리는 나중에 배웁니다. 2의 보수를 사용하게 되면, 원래 0부터 255까지 표현할 수 있었던 걸, -128부터 127까지 표현할 수 있는 자료형으로 바꿀 수 있습니다. 즉, 원래 표현할 수 있었던 값의 범위의 반을 음수를 표현하는데 사용하는 것입니다. 마찬가지로 0부터 65535까지 표현할 수 있었던 자료형은 -32768부터 32767까지 표현하는 자료형으로 바꿀 수 있습니다.
C에서 변수를 정의하는 방법은 다음과 같습니다.
<변수의 자료형> <변수의 이름> [ = <초기식>] [, <또 다른 변수의 이름> [ = <초기식>]] ...;
앞으로 문법을 설명할 때는 이렇게 꺽쇠
< >
와 대괄호[ ]
를 이용하여 표시할 예정입니다. 꺽쇠 안에는 단어나표현식(expression)
이 들어갈 수 있습니다. 대괄호 안은 생략할 수 있는 부분을 나타냅니다.생략 부호(..., ellipsis)
는 반복되는 패턴을 나타냅니다.
위에 있는 변수 사용 설명이 잘 안 와닿을 것이라고 생각합니다. 실제 사용 예시를 보도록 합시다.
int main()
{
char c;
short s = 0;
double d, dd = 0.2;
int i = 0, j = 1, k;
}
이렇게 char
, short
, double
, int
같은 변수의 자료형이 먼저 오고, c
, s
, d
, dd
, i
, j
, k
라는 변수의 이름이 옵니다. 그리고 나서, 등호가 나온 후 값이 나오는데요, 이것은 변수가 나타내는 메모리의 공간에 그 값을 집어넣으라는 의미입니다. 예를 들어 s
가 2000번 공간을 나타낸다면, 컴퓨터가 2000번 공간을 s
라고 이름지은 직후 2000번 공간엔 0이 들어있게 됩니다. 여기서 dd
앞에는 자료형이 없지만 d
와 같은 double
이고, j
, k
도 마찬가지로 자료형이 int
입니다. 반대로, 초기식이 없는 상태에서 그 변수를 사용하게 되면, 그 공간을 이름짓기 전에 어떤 값이 들어있을 지 모르는 상황이 발생하게 됩니다. 이 때 그 값을 쓰레기값(garbage value)
라고 부릅니다. 이 쓰레기값은 버그의 원인 중 하나입니다.
C의 정수 자료형은 다음과 같습니다.
이름 | 바이트 수 | 저장할 수 있는 값 범위 |
---|---|---|
signed char |
1 | -128(-2⁷) ~ 127(2⁷ - 1) |
signed short int |
2 | -32768(-2¹⁵) ~ 32767(2¹⁵ - 1) |
signed long int |
4 | -2³¹ ~ 2³¹ - 1 |
signed long long int |
8 | -2⁶³ ~ 2⁶³ - 1 |
자료형의 int
와 signed
는 생략할 수 있습니다. 즉, char
, short int
, long int
, long long int
이라 적어도 되고, short
, long
, long long
이라 적어도 됩니다.
int main()
{
signed char c = -5;
short int s = 6;
signed long l = -7;
long long int ll = 8;
}
signed
대신에 unsigned
를 쓰면 양의 정수만 표현하는 자료형이 됩니다.
이름 | 바이트 수 | 저장할 수 있는 값 범위 |
---|---|---|
unsigned char |
1 | 0 ~ 255(2⁸ - 1) |
unsigned short int |
2 | 0 ~ 65535(2¹⁶ - 1) |
unsigned long int |
4 | 0 ~ 2³² - 1 |
unsigned long long int |
8 | 0 ~ 2⁶⁴ - 1 |
이 경우엔 unsigned
는 생략할 수 없지만, int
는 생략할 수 있습니다.
int main()
{
unsigned char c = 5;
unsigned short int s = 6;
unsigned long l = 7;
unsigned long long ll = 8;
}
signed int
자체도 자료형이 됩니다. signed int
의 바이트 수는 컴파일러에 따라 다릅니다. 아두이노에선 2이지만, Visual Studio의 컴파일러를 포함한 요즘 나오는 대부분의 컴파일러에선 4입니다. 즉 signed int
와 signed long int
는 표현하는 범위가 같습니다. signed int
도 줄여서 int
라고 쓸 수 있습니다. 특수한 경우에선, int
자체를 생략할 수 있는데, 이것도 나중에 다루도록 하겠습니다. signed int
가 있는 것처럼 unsigned int
도 있습니다. unsigned int
도 줄여서 unsigned
라고 쓸 수 있습니다.
int main()
{
int i = 5;
unsigned u = 6;
unsigned int uu = 7;
}
사실 컴퓨터에선 완벽한 실수를 처리하지 못합니다. 수학에 관심있는 분들은 다들 아시겠지만, 정수 집합을 정의역으로, 실수 집합을 공역으로 하는 일대일대응은 존재하지 않습니다. 즉, 실수를 온전하게 정수로 바꿔서 표현할 수 없다는 의미입니다. 그래서 컴퓨터에선 부동소수점
이란 체계를 사용하고, 실수를 그 값에 가까운 소수로 표현해서 사용합니다. 부동소수점
자체에 대한 건 나중에 다룰 예정입니다.
이름 | 바이트 수 | 저장할 수 있는 값 범위 |
---|---|---|
float |
4 | ± 3.4 ÷ 10³⁸ ~ ± 3.4 × 10³⁸ |
double |
8 | ± 1.7 ÷ 10³⁰⁸ ~ ± 1.7 × 10³⁰⁸ |
int main()
{
float f = 0.5;
double d = -1.1237;
}
C에서 변수의 이름을 지을 땐 몇가지 규칙을 지켜야 합니다.
- 변수의 이름은 문자와 언더스코어(_), 숫자로 이루어져있습니다.
- 변수의 이름은 숫자로 시작해서는 안됩니다.
- 변수의 이름은 대소문자를 구분합니다.
다음은 적절한 변수 이름의 예시입니다.
int main()
{
double my_first_variable_0;
int 내_첫번째_변수1;
char _2_my_second_var;
long long _;
}
다음은 적절하지 않은 변수 이름의 에시입니다.
int main()
{
short is+this+a+variable
unsigned 2nd_var;
float 3번째-변수;
}
변수를 화면에 출력할 때에도 3장에서 배운 printf
를 활용 할 수 있습니다. 다음 코드를 입력하신 후 Ctrl + F5
를 눌러 결과를 확인해주세요.
#include <stdio.h>
int main()
{
int i = 5;
printf("Value of i: %d", i);
}
출력 결과가 다음과 같아야 합니다.
Value of i: 5
printf
도 여러 번 사용할 수 있습니다.
#include <stdio.h>
int main()
{
int i = 5, j = 6;
printf("Value of i: %d ", i);
printf("Value of i: %d ", i);
printf("Value of j: %d ", j);
}
Value of i: 5 Value of i: 5 Value of j: 6
이렇게 %d
라는 표시를 한 곳에 %d
가 표시되는 것이 아니라, 변수의 값이 표시되는 것을 알 수 있습니다. %d
는 여러 번 사용할 수 있습니다.
#include <stdio.h>
int main()
{
int i = 5, j = 6;
printf("Value of i and j: %d, %d", i, j);
}
Value of i and j: 5, 6
%d
는 int
를 표시하기 위해 사용됩니다. 자료형이 다른 변수는 다른 표시를 사용하여야 합니다. 이러한 표시들을 형식 지정자(format specifier)
라고 합니다. 자료형에 따른 형식 지정자는 다음과 같습니다. char
의 형식 지정자는 나중에 배우게 됩니다.
이름 | 형식 지정자 |
---|---|
signed int |
%d |
signed short int |
%hd |
signed long int |
%ld |
signed long long int |
%lld |
unsigned int |
%u |
unsigned short int |
%hu |
unsigned long int |
%lu |
unsigned long long int |
%llu |
float |
%f |
double |
%lf |
이 형식 지정자를 모두 섞어 쓸 수 있습니다.
#include <stdio.h>
int main()
{
int i = 6;
double d = 7;
unsigned long long ull = 8;
printf("%d, %lf, and %llu", i, d, ull);
}
6, 7.000000, and 8