C 문자열 완벽 이해: 널 종결 문자 배열과 표준 라이브러리 함수
C에서 문자열은 널 종결 문자('\0')로 끝나는 char 배열입니다. 별도 문자열 타입이 없으며, 표준 라이브러리 함수들이 이 관례를 따릅니다.
문자열 표현과 메모리
// 1. 문자 배열로 초기화 (수정 가능, 스택)
char str1[] = "Hello"; // 크기 6: 'H','e','l','l','o','\0'
char str2[10] = "Hi"; // 크기 10, 나머지 0
// 2. 포인터로 문자열 리터럴 가리킴 (수정 불가, 읽기 전용 영역)
char *str3 = "Hello"; // 문자열 리터럴 주소 저장
// str3[0] = 'h'; // UB! 세그먼트 폴트 발생 가능
// 3. 동적 할당 문자열
char *str4 = malloc(20);
strcpy(str4, "Dynamic");
free(str4);
메모리 레이아웃
str1 (스택): [H][e][l][l][o][\0][?][?]...
0 1 2 3 4 5 6 7
str3 (로데이터): [H][e][l][l][o][\0] (읽기 전용)
핵심 문자열 함수 (<string.h>)
| 함수 | 원형 | 동작 | 반환 | 주의사항 |
strlen | size_t strlen(const char *s) | 널 제외 길이 계산 | 길이 | O(n), 널 없으면 UB |
strcpy | char *strcpy(char *dest, const char *src) | src를 dest에 복사 (널 포함) | dest | 버퍼 오버플로 위험! strncpy 또는 strlcpy 권장 |
strncpy | char *strncpy(char *dest, const char *src, size_t n) | 최대 n자 복사 | dest | src<n이면 나머지는 0 채움, src≥n이면 널 미포함 가능 |
strcat | char *strcat(char *dest, const char *src) | dest 끝에 src 추가 | dest | dest 공간 충분해야, strncat 권장 |
strncat | char *strncat(char *dest, const char *src, size_t n) | 최대 n자 추가 | dest | 널 보장 |
strcmp | int strcmp(const char *s1, const char *s2) | 사전순 비교 | <0, =0, >0 | 대소문자 구분, 로케일 무관 |
strncmp | int strncmp(const char *s1, const char *s2, size_t n) | 최대 n자 비교 | <0, =0, >0 | 접두사 비교에 유용 |
strchr | char *strchr(const char *s, int c) | 문자 c 첫 출현 위치 | 포인터/NULL | 널도 검색 가능 |
strrchr | char *strrchr(const char *s, int c) | 문자 c 마지막 출현 | 포인터/NULL | - |
strstr | char *strstr(const char *haystack, const char *needle) | 부분 문자열 검색 | 포인터/NULL | - |
strtok | char *strtok(char *str, const char *delim) | 문자열 토큰화 | 토큰/NULL | 원본 수정, 스레드 안전 아님 (strtok_r 사용) |
memcpy | void *memcpy(void *dest, const void *src, size_t n) | 메모리 블록 복사 | dest | 겹치는 영역 불가, memmove 사용 |
memmove | void *memmove(void *dest, const void *src, size_t n) | 겹침 안전 복사 | dest | 항상 안전 |
memset | void *memset(void *s, int c, size_t n) | 메모리 설정 | s | 바이트 단위 설정 |
memcmp | int memcmp(const void *s1, const void *s2, size_t n) | 메모리 비교 | <0, =0, >0 | 이진 비교 |
안전한 문자열 처리 패턴
버퍼 오버플로 방지
// 위험: strcpy(dest, src); // dest 크기 모름
// 안전 1: strncpy + 강제 널 종결
char dest[20];
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
// 안전 2: snprintf (C99, 가장 권장)
snprintf(dest, sizeof(dest), "%s", src); // 자동 널 종결, 길이 반환
// 안전 3: strlcpy/strlcat (BSD 확장, 표준 아님 but 널리 사용)
strlcpy(dest, src, sizeof(dest));
문자열 연결 (여러 조각)
char buf[256];
buf[0] = '\0';
strncat(buf, part1, sizeof(buf) - strlen(buf) - 1);
strncat(buf, part2, sizeof(buf) - strlen(buf) - 1);
// 또는 snprintf로 한 번에
snprintf(buf, sizeof(buf), "%s%s%s", part1, part2, part3);
문자열 파싱/변환
| 함수 | 용도 | 헤더 |
atoi/atol/atof | 문자열→숫자 (에러 검출 불가) | stdlib.h |
strtol/strtoul/strtoll | 문자열→정수 (에러/진수 검출 가능) | stdlib.h |
strtof/strtod/strtold | 문자열→실수 (에러 검출 가능) | stdlib.h |
sprintf/snprintf | 숫자→문자열 포맷팅 | stdio.h |
// 안전한 정수 파싱
char *endptr;
long val = strtol(str, &endptr, 10);
if (endptr == str || *endptr != '\0') {
// 변환 실패 또는 남은 문자 있음
}
유니코드/멀티바이트 문자열
C11: char16_t, char32_t, u"...", U"...", <uchar.h>
멀티바이트: mbstowcs, wcstombs, <wchar.h>
UTF-8은 char 배열로 저장, 1문자=1~4바이트 → strlen은 바이트 수 반환
모범 사례
고정 버퍼 사용 시 snprintf, strncat으로 크기 제한
동적 문자열: 크기 추적 구조체(len, cap, data) 직접 구현 또는 라이브러리 사용
문자열 리터럴은 const char *로 받기
strtok 대신 strtok_r (POSIX) 또는 직접 파싱
문자열 비교 시 strcmp 반환값을 == 0으로 검사 (!strcmp() 가독성 나쁨)
C 언어
-
C# 문자열(String) 마스터: 기초, 연산, 고급 기법 총정리
-
Java String 마스터하기: 생성, 메서드, 모범 사례
-
Java Enum 문자열 표현 마스터: 기본값, 오버라이딩, 고급 패턴
-
VHDL 문자열 리스트 구현: 모범 사례와 예제
-
C++ 문자열 함수 마스터: strcpy, strcat, strlen, strcmp 실전 예제
-
C 문자열 완전 정복: 선언, 입출력, 라이브러리 함수
-
Java String.charAt() 완전 이해: 문법, 반환타입, 예외, 실전 예제
-
Java String.endsWith() 마스터: 문자열 접미사 확인 예제
-
Python 문자열 연산 마스터하기: replace, join, split, reverse, 대소문자 변환
-
Python str.count() 마스터하기: 문자 및 부분문자열 개수 세기 예제