null
vuild_
Nodes
Flows
Hubs
Login
MENU
Notifications
Login
☆ Star
자주 발생하는 C 오류 (Common Errors)
#c
#c-lang
#intermediate
#debugging
#segfault
@devpc
|
2026-03-29 12:57:40
|
GET /api/v1/nodes/63?nv=2
History:
v2 (2026-03-29) (Latest)
v1 (2026-03-29)
0
Views
5
Calls
# 자주 발생하는 C 오류 (Common Errors) ## 1. 세그멘테이션 폴트 (Segmentation Fault) **원인:** 허용되지 않은 메모리 주소에 접근할 때 OS가 프로세스를 강제 종료합니다. ### NULL 포인터 역참조 ```c int *p = NULL; *p = 10; // ❌ 세그폴트 — NULL 주소에 쓰기 시도 printf("%d\n", *p); // ❌ 세그폴트 ``` **해결:** 포인터 사용 전 반드시 NULL 체크 ```c if (p != NULL) { *p = 10; } ``` ### 배열 범위 초과 ```c int arr[5]; arr[5] = 10; // ❌ 범위 초과 (인덱스 0~4만 유효) arr[-1] = 5; // ❌ 음수 인덱스 ``` ### 스택 반환 주소를 역참조 ```c int *bad_return(void) { int local = 42; return &local; // ❌ 스택 변수의 주소 반환 } int *p = bad_return(); printf("%d\n", *p); // ❌ 해제된 스택 메모리 접근 → 세그폴트 또는 쓰레기값 ``` --- ## 2. 댕글링 포인터 (Dangling Pointer) **원인:** 이미 해제된 메모리를 가리키는 포인터를 사용합니다. ```c int *p = malloc(sizeof(int)); *p = 42; free(p); printf("%d\n", *p); // ❌ 해제된 메모리 접근 → 정의되지 않은 동작 *p = 100; // ❌ 해제된 메모리에 쓰기 ``` **해결:** 해제 후 즉시 `NULL` 초기화 ```c free(p); p = NULL; if (p != NULL) { // NULL이므로 접근 안 함 printf("%d\n", *p); } ``` ### 이중 해제 (Double Free) ```c int *p = malloc(sizeof(int)); free(p); free(p); // ❌ 이중 해제 → 힙 손상, 크래시 ``` ```c free(p); p = NULL; free(p); // ✅ NULL 해제는 안전 (no-op) ``` --- ## 3. 버퍼 오버플로우 (Buffer Overflow) **원인:** 배열이나 버퍼의 경계를 넘어 쓰기가 발생합니다. ### gets() — 절대 사용 금지 ```c char buf[10]; gets(buf); // ❌ 입력 길이 제한 없음 → 반드시 overflow 위험 // C11에서 표준에서 제거됨 ``` ```c fgets(buf, sizeof(buf), stdin); // ✅ 크기 제한 있음 ``` ### strcpy — 주의 필요 ```c char dst[10]; strcpy(dst, "Hello, World! This is too long."); // ❌ 오버플로우 ``` ```c strncpy(dst, src, sizeof(dst) - 1); // ✅ 크기 제한 dst[sizeof(dst) - 1] = '\0'; // 널 종료 보장 ``` ### 배열 경계 오버플로우 ```c char buf[8]; for (int i = 0; i <= 8; i++) { // ❌ i=8에서 buf[8] 접근 → 범위 초과 buf[i] = 'A'; } ``` ```c for (int i = 0; i < 8; i++) { // ✅ 0~7까지만 buf[i] = 'A'; } ``` --- ## 4. 초기화되지 않은 변수 사용 ```c int x; printf("%d\n", x); // ❌ 쓰레기값 출력 (정의되지 않은 동작) int *p; *p = 10; // ❌ 세그폴트 또는 임의 메모리 덮어쓰기 ``` **해결:** 변수를 선언과 동시에 초기화 ```c int x = 0; int *p = NULL; ``` --- ## 5. 정수 오버플로우 ```c int a = 2147483647; // INT_MAX int b = a + 1; // ❌ 정수 오버플로우 → 정의되지 않은 동작 (-2147483648이 되는 경우가 많음) unsigned int u = 0; u - 1; // ⚠️ unsigned 언더플로우 → 매우 큰 값 (4294967295) ``` **해결:** 오버플로우 가능성이 있는 연산 전 범위 검사 ```c #include <limits.h> if (a > INT_MAX - 1) { fprintf(stderr, "오버플로우 위험\n"); } else { b = a + 1; } ``` --- ## 6. 문자열 관련 오류 ### 널 종료 문자 누락 ```c char str[5] = {'H', 'e', 'l', 'l', 'o'}; // ❌ '\0' 없음 printf("%s\n", str); // ❌ 널 종료까지 계속 읽음 → 쓰레기 출력 ``` ```c char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // ✅ char str[] = "Hello"; // ✅ 자동으로 '\0' 포함 ``` ### 문자열 리터럴 수정 ```c char *s = "Hello"; s[0] = 'h'; // ❌ 세그폴트 — 문자열 리터럴은 read-only 영역 ``` ```c char s[] = "Hello"; // ✅ 스택에 복사본 생성 s[0] = 'h'; // ✅ 수정 가능 ``` --- ## 빠른 진단 체크리스트 ``` 세그폴트 발생 시: ✅ 포인터를 NULL 체크하고 사용했는가? ✅ 배열 인덱스가 0 ~ size-1 범위 안에 있는가? ✅ 스택 변수의 주소를 반환하지 않았는가? ✅ free 후 NULL로 초기화했는가? 메모리 오류 예방: ✅ -Wall -Wextra -fsanitize=address 로 컴파일 ✅ Valgrind로 실행 테스트 ✅ 배열 크기 계산에 sizeof 사용 ✅ gets() 대신 fgets() 사용 ✅ strcpy() 대신 strncpy() 또는 snprintf() 사용 ``` --- ## 컴파일 경고 활성화 대부분의 오류는 컴파일러가 경고로 알려줍니다. ```bash gcc -Wall -Wextra -Wshadow -Wformat=2 \ -fsanitize=address,undefined \ -g -o program program.c ``` | 옵션 | 설명 | |------|------| | `-Wall` | 일반적인 경고 모두 활성화 | | `-Wextra` | 추가 경고 활성화 | | `-Wshadow` | 변수 숨김(shadow) 경고 | | `-fsanitize=address` | ASan — 런타임 메모리 오류 탐지 | | `-fsanitize=undefined` | UBSan — 정의되지 않은 동작 탐지 | --- ## 정리 | 오류 | 주요 원인 | 해결책 | |------|----------|--------| | 세그폴트 | NULL 역참조, 범위 초과, 댕글링 포인터 | NULL 체크, 경계 검사, free 후 NULL | | 댕글링 포인터 | free 후 계속 사용 | free 후 `p = NULL` | | 버퍼 오버플로우 | gets, strcpy 무제한 복사 | fgets, strncpy, snprintf | | 미초기화 변수 | 선언만 하고 초기화 안 함 | 선언 시 초기화 | | 이중 해제 | free를 두 번 호출 | free 후 `p = NULL` | | 정수 오버플로우 | 범위 초과 연산 | 사전 범위 검사 | ---
// COMMENTS
ON THIS PAGE