23 변수 선언
먼저 알아야 할 것
돌아보기
5장에서 메모리는 번호(주소) 붙은 사물함들이라 했다. 그런데 지금까지 쓴 C 코드에는 주소가 한 번도 등장하지 않았다 — 그러면 프로그램은 사물함을 어떻게 쓰고 있었던 것인가?
답. 이름을 통해서다. C에서 변수를 선언한다는 것은 “사물함 몇 칸을 잡고, 거기에 사람이 읽을 이름을 붙이는” 일이다. 번호(주소)는 컴파일러가 관리하고, 우리는 이름으로 부른다 — 이름은 주소의 사람용 별명이다. 이 장에서 그 이름을 만드는 법을 배우고, 별명 뒤의 진짜 주소를 다시 만나는 것은 제7부(35장)다.
이 장의 필요성과 맥락
= 를 여기서 정확히 못박아야 34장까지 이어지는 오해가 애초에 생기지 않는다.이 장이 끝나면
=. 그리고 이 =가 수학의 등호가 아니라는 것이 이 장의 가장 중요한 문장이다.이 장에서 답할 질문
- 타입 이야기에서
int가 “약 ±21억”이라 했는데 — 정확한 크기가 기계마다 다르다는 뜻인가? 7장에서 배운 8비트, 16비트 이야기는 어디로 갔는가?
23.1 선언 — 타입, 이름, 그리고 첫 값
변수 선언의 문법은 이미 여러 번 스쳐 본 모양이다:
int apples = 12;세 부분으로 읽는다 — 타입(int), 이름(apples), 그리고 초기화 (= 12). 뜻은: “정수를 담는 칸을 잡고, apples라 부르기로 하고, 첫 값으로 12를 담아라.”
타입은 두 가지를 약속한다. 첫째, 값의 집합 — int는 부호 있는 정수의 그릇으로, 흔한 기계에서 32비트, 즉 7장의 2의 보수 세계에서 약 ±21억 범위다. double이라 적으면 8장의 64비트 부동소수점 그릇이 된다. 둘째, 연산의 약속 — 그 이름에 무엇을 해도 되는지(정수 산술인지 근사 산술인지)가 타입에서 나온다. 5장에서 “덩어리를 아는 것은 읽는 쪽”이라 했는데, C에서는 타입이 바로 그 읽는 눈이다.
이름은 자유롭게 짓되 문법이 정한 틀 안에서다 — 글자·숫자·밑줄로 이루어지고 숫자로 시작할 수 없다. 좋은 이름은 문법이 아니라 사람을 위한 것이다: a보다 apples가 반년 뒤의 자신에게 친절하다.
초기화는 관행이 아니라 이 책의 규칙이다. 선언할 때 반드시 첫 값을 담아라. 초기화 없이 선언만 한 변수의 칸에는 그 자리에 우연히 남아 있던 쓰레기 비트가 들어 있고(5장 — 칸은 언제나 무언가로 차 있다), 그것을 읽는 것은 대표적인 사고 경로다. 정식 취급은 43장에서 하되, 습관은 지금부터 들인다.
23.2 대입 — 상태를 바꾸는 부수효과
선언된 변수의 값은 대입(=)으로 바꾼다. 20장의 배분표에 예고됐던 연산자가 드디어 합류하는 것이다. 시연부터:
examples/ch23/var.c
#include <stdio.h>
int main(void)
{
int apples = 12; /* 선언과 동시에 초기화한다 */
printf("apples: %d\n", apples);
apples = apples + 3; /* 새 값 = 지금 값 + 3 */
printf("apples: %d\n", apples);
return 0;
}
실행 결과
apples: 12
apples: 15
셋째 문장 apples = apples + 3;을 정확히 읽는 것이 이 장의 핵심이다. 읽는 순서는 — ① 오른쪽 수식 apples + 3을 평가한다(지금 값 12를 읽어 15를 얻는다), ② 그 값을 왼쪽 이름의 칸에 담는다(원래 있던 12는 덮여 사라진다). 즉 =는 “같다”가 아니라 “오른쪽을 계산해 왼쪽에 담아라”라는 지시다. 값이 바뀌는 것 — 이것이 20장에서 예고한, 출력 말고 두 번째로 만나는 부수효과(side effect)다: 대입은 변수의 상태를 바꾼다.
흔한 오해. “x = x + 1은 말이 안 되는 식이다 — 어떤 수도 자기 더하기 1과 같을 수 없으니까”
= 기호의 재활용이다. C의 =는 등호가 아니라 담기 지시라서, x = x + 1은 “x의 지금 값에 1을 더해, 그 결과를 x에 도로 담아라” — 즉 x를 1 키우라는 멀쩡한 명령이다. 오른쪽의 x는 읽을 때의 값(옛 값)이고 왼쪽의 x는 담을 곳(칸)이라는 것 — 같은 이름이 자리에 따라 값과 장소로 다르게 읽히는 이 구별이 C 읽기의 기본기다. 참고로 “같은가?”를 묻는 진짜 비교는 다른 기호(==, 30장)가 맡는다 — 기호를 나눠 쓴 이유가 바로 이 혼동 때문이다.23.3 const — 바꾸지 않겠다는 약속
바꿀 일이 없는 값에는 선언에 const를 붙인다:
const int max_floor = 63; /* 이 이름의 값은 앞으로 바뀌지 않는다 */const가 붙은 이름에 대입하려 하면 컴파일러가 오류로 막는다. 이것은 자물쇠라기보다 문서다 — “이 값은 변하지 않음”을 컴파일러와 다음 읽는 사람 모두에게 알리는 것이다. 바뀌지 않는 것이 많은 코드일수록 읽기 쉽고 사고가 적다 — 그래서 현대적 관행은 “일단 const로 선언하고, 바꿔야 할 것만 뺀다”에 가깝다. 이 책의 예제도 그 감각을 따른다.
문. 타입 이야기에서 int가 “약 ±21억”이라 했는데 — 정확한 크기가 기계마다 다르다는 뜻인가? 7장에서 배운 8비트, 16비트 이야기는 어디로 갔는가?
답. 정확한 관찰이다. int의 크기는 표준이 “최소한 이만큼”만 정하고 구체 크기는 플랫폼에 맡긴 타입이다 — 지금 주류 환경에서는 32비트가 사실상의 표준이다. 7장의 8비트·16비트 그릇들, 그리고 “크기를 정확히 못박고 싶을 때”의 타입들(int32_t 같은)은 27장에서 정수 타입 가족 전체를 소개하며 정리한다. 이 부에서는 int 하나로 충분하다 — 나선형 원칙대로, 가족 상봉은 필요해질 때 한다.
이름을 만들었고, 값을 담았고, 바꾸는 법도 배웠다. 그런데 15장부터 끌고 온 외상 장부에는 아직 두 줄이 남아 있다 — int main(void)와 return 0. 둘 다 함수의 문법이다. 변수에 이름을 붙였으니, 다음 장에서는 일에 이름을 붙인다 — 함수를 스스로 만드는 법, 그리고 외상 장부의 완전한 청산이다.