char 배열이고, 왜 '\0'가 필수인지const char*(문자열 리터럴)과 char[](수정 가능한 버퍼)의 차이const 위치에 따른 의미를 정확히 읽는 법strlen/strcpy/strcat의 동작과 가장 흔한 버그(버퍼 오버플로우)'\0'로 끝나는 char 배열” )const char* / char* const / const char* const를 구분할 수 있다.'\0'(널 종료 문자)로 끝나는 char 배열입니다.'\0'가 없으면 strlen, strcpy 같은 함수는 “어디까지가 문자열인지”를 모르고 메모리를 계속 읽다가 사고가 납니다.예시:
char str1[] = {'H','e','l','l','o','\0'};
char str2[] = "Hello"; // 컴파일러가 자동으로 '\0'을 붙여줌
핵심 체크:
"Hello"의 실제 길이(문자 수)는 5지만, 버퍼에는 '\0'까지 포함되어 6칸이 필요합니다.| 선언 | 저장 위치 | 수정 |
|---|---|---|
const char* ptr = "Hello" | 문자열 리터럴은 보통 읽기 전용 메모리(.rdata 등). ptr은 그 주소를 가리킴 | 수정 금지(수정 시 UB) |
char str[] = "Hello" | 수정 가능한 버퍼(지역이면 보통 스택)에 복사본 생성 | 수정 가능 |
const char*로 받는 게 기본입니다.const char* p = "Hello";
// p[0] = 'h'; // ❌ UB (절대 하지 말 것)
char s[] = "Hello";
s[0] = 'h'; // ✅ OK (복사본 수정)
const char* a; // 가리키는 내용 수정 불가, 주소 변경 가능
char* const a; // 주소 변경 불가, 가리키는 내용은 수정 가능
const char* const a; // 주소·내용 모두 불변
const를 읽는 안전한 규칙(암기):
const는 바로 옆의 대상을 상수화합니다.예:
const char* a = “char는 const(내용 불변), 포인터는 가변”char* const a = “포인터는 const(주소 불변), 내용은 가변”strlen(str): '\0' 전까지의 문자 수(글자 수)를 셉니다.strcpy(dest, src): src를 dest로 복사합니다. '\0'까지 포함해서 복사합니다.strcat(dest, src): dest 끝에 src를 이어붙입니다. 역시 dest의 버퍼 크기가 충분해야 합니다.가장 중요한 위험:
strcpy/strcat은 기본적으로 “버퍼 크기”를 모르기 때문에, 크기 검증 없이 쓰면 버퍼 오버플로우로 이어질 수 있습니다.'\0'는 왜 반드시 필요할까?const char* p = "Hello";에서 p[0]을 바꾸면 왜 위험할까?strlen("Hello")는 5일까 6일까? 그 이유는?