C에서 char a[] = "cat";는 세 글자 뒤에 null 문자를 포함하는 네 칸 배열을 만듭니다. 반면 char b[3] = "cat";는 세 글자만 담는 세 칸 배열이며 null 종료 문자가 들어가지 않습니다.
sizeof(a)와 sizeof(b)는 배열이 보이는 이 선언 지점에서 각각 4와 3을 나타냅니다. strlen은 null 종료된 문자열을 전제로 하므로 b를 C 문자열이라고 가정해 결과를 예측하거나 안전하게 사용할 수 없습니다.
문자 수와 null 종료에 필요한 저장 공간을 구분합니다
화면에 보이는 cat은 문자 세 개입니다. 하지만 C 문자열은 글자만 나열한 배열이 아니라 끝을 알리는 null 문자 \0까지 포함한 연속된 문자 시퀀스입니다. 따라서 세 글자를 C 문자열로 저장하려면 일반적인 문자 배열 표현에서 네 칸이 필요합니다. \0은 문자열의 마지막을 표시하지만 strlen이 세는 길이에는 포함되지 않습니다.
C11 N1570 §6.4.5p6은 문자열 리터럴의 문자 시퀀스 끝에 값 0인 byte를 붙여 배열을 초기화한다고 규정합니다. 같은 문서의 §6.7.9 Example 8은 char s[] = "abc"를 네 칸, char t[3] = "abc"를 세 칸으로 보여 줍니다. 두 배열의 크기를 각각 문자열 길이에 맞출 수 있지만, 뒤의 고정 크기 배열에는 null 종료 문자가 들어갈 자리가 없습니다.
| 선언 | 배열 칸 | 내용 | C 문자열 여부 |
|---|---|---|---|
| char a[] = "cat"; | 4 | 'c', 'a', 't', '\0' | null 종료됨 |
| char b[3] = "cat"; | 3 | 'c', 'a', 't' | null 종료되지 않음 |
#include <stdio.h>
#include <string.h>
int main(void) {
char a[] = "cat";
char b[3] = "cat";
printf("sizeof(a)=%zu strlen(a)=%zu\n", sizeof a, strlen(a));
printf("sizeof(b)=%zu\n", sizeof b);
/* b is not null-terminated; do not pass it to strlen. */
return 0;
}sizeof는 선언된 배열 칸을 보고 strlen은 종료 문자를 찾습니다
a는 크기를 생략했으므로 초기화 항목을 담을 만큼 배열 경계가 정해집니다. cat의 문자 세 개와 문자열 리터럴의 마지막 null byte가 들어가 총 네 칸입니다. 선언 지점에서 sizeof a는 배열 전체 크기인 4를 나타내고 strlen(a)는 null 앞의 문자 수인 3을 셉니다.
b는 선언에서 배열 경계를 3으로 고정했습니다. 초기화 뒤 세 칸은 c, a, t이고 종료 문자가 배열에 포함되지 않습니다. sizeof b는 여전히 선언된 배열 전체의 크기 3을 알려 줍니다. 하지만 strlen(b)를 호출하면 함수가 배열 경계를 모른 채 null 문자를 찾게 됩니다. b의 저장 범위 안에서 끝 문자가 없으므로 그 호출의 문자열 전제는 충족되지 않습니다. 이 글은 해당 호출의 결과를 실행하거나 예측하지 않습니다.
배열 크기와 문자열 함수의 전제를 순서대로 확인합니다
- 문자열 리터럴의 보이는 문자를 세고, 끝에 추가되는
\0을 별도로 표시합니다. - 배열 선언이 크기를 생략했는지, 숫자로 고정했는지 확인합니다.
- 초기화 문자와 종료 문자가 모두 선언된 칸 안에 들어가는지 그립니다.
sizeof를 배열이 실제로 보이는 선언 지점에서 사용하는지 확인합니다.strlen이나%s처럼 C 문자열을 요구하는 연산은 null 종료된 배열에만 적용합니다.
풀이할 때 메모리에 칸을 직접 그리는 방법이 가장 단순합니다. a는 네 칸을 만들어 [c][a][t][\0]으로 적고, b는 [c][a][t] 세 칸에서 멈춥니다. 여기서 sizeof의 숫자와 문자열 길이의 숫자는 다르게 나옵니다. 네 칸인 a의 문자열 길이는 3이고, b의 배열 저장 공간은 3이지만 그것만으로 올바른 C 문자열이 되지는 않습니다. 각 연산이 묻는 값의 종류를 먼저 적으세요.
세 칸 배열이 항상 초기화 오류라고 단정하지 않습니다
흔한 오해는 char b[3] = "cat";가 무조건 선언 오류이므로 컴파일 결과가 없다고 보는 것입니다. N1570 예제는 이 배열 선언을 보여 주며, 세 문자만 담는 배열로 구별합니다. 선언이 허용되는지와 그 배열이 null 종료 C 문자열인지 여부는 다른 질문입니다. 세 칸이 실제로 존재해도 종료 문자를 담지 않았다면 문자열을 요구하는 함수의 입력이 될 수 없습니다.
또 sizeof와 strlen을 서로 대체할 수 있다고 생각하기 쉽습니다. sizeof는 해당 식에서 배열이면 배열의 전체 크기를 바이트 단위로 계산하지만, 함수 매개변수로 전달된 배열은 포인터로 조정되는 등 적용 문맥이 달라질 수 있습니다. strlen은 null 종료된 문자열의 종료 지점까지 세므로, 종료 문자가 없는 배열에는 안전한 길이 측정법이 아닙니다. 길이를 별도 정수로 전달하거나 넉넉한 배열을 만들고 경계를 지키는 방법을 선택하세요.
정답을 가리고 확인해 보세요
이 글을 위해 만든 학습 예시이며 공식 기출문제나 판매 카드의 인용이 아닙니다.
char word[4] = "cat";의 배열 칸과 sizeof word, strlen(word)의 값을 설명하세요. char word[3]이면 무엇이 달라지나요?
정답과 해설 확인
네 칸 배열에는 c, a, t, \0이 들어가며 sizeof는 4, strlen은 3입니다. 세 칸 배열은 c, a, t만 담아 null 종료되지 않으므로 C 문자열로 쓰거나 strlen 결과를 안전하게 예측할 수 없습니다.N1570 §6.4.5p6은 문자열 리터럴 뒤의 null byte를 설명하고 §6.7.9 Example 8은 크기 생략 배열과 3칸 배열을 직접 비교합니다. 문자열은 null 종료를 요구합니다.