숏컷 코드
calloc은 “0으로 시작하는 새 버퍼”, realloc은 “기존 버퍼 크기 조정”으로 읽으면 됩니다. realloc은 실패 시 원본을 잃지 않는 패턴이 핵심입니다.
int *arr = calloc(10, sizeof(int));
int *tmp = realloc(arr, 20 * sizeof(int));
if (tmp == NULL) {
free(arr);
return NULL;
}
arr = tmp;문법
calloc(n, size)는 할당과 동시에 메모리 바이트를 0으로 채웁니다. 정수 카운터 배열이나 NULL로 시작하는 포인터 배열처럼 "0으로 시작하는 버퍼"가 의미 있는 경우라면 malloc보다 의도가 더 직접적으로 드러납니다.
size_t count = 8;
int *histogram = calloc(count, sizeof(*histogram));
if (histogram == NULL) {
return 1;
}반대로 0 초기화가 필요 없고 곧바로 덮어쓸 버퍼라면 malloc도 충분합니다.
동작 차이
realloc은 기존 메모리 블록의 크기를 조절할 때 씁니다. 성공하면 같은 위치를 유지할 수도 있고, 새 위치로 옮길 수도 있습니다. 그래서 원본 포인터에 바로 덮어쓰지 않는 패턴이 필수입니다.
size_t capacity = 4;
int *buf = malloc(sizeof(*buf) * capacity);
if (buf == NULL) {
return 1;
}
capacity *= 2;
int *tmp = realloc(buf, sizeof(*buf) * capacity);
if (tmp == NULL) {
free(buf);
return 1;
}
buf = tmp;이 카드의 핵심은 calloc의 초기 상태와 realloc의 실패/이동 가능성을 같이 읽는 데 있습니다.
- 0으로 채운 새 버퍼가 필요하면
calloc을 먼저 본다. - 기존 버퍼를 키우거나 줄이면
realloc을 본다. realloc은 성공 시 포인터가 바뀔 수 있으므로 임시 변수 패턴을 쓴다.- 동적 배열 용량은 보통 조금씩이 아니라 2배 정도로 키우는 편이 낫다.
- 실패 시 원본 버퍼가 아직 살아 있는지 먼저 확인한다.
주의할 점
ptr = realloc(ptr, new_size)처럼 직접 대입하면 실패 시 기존 메모리를 잃을 수 있습니다. realloc이 NULL을 반환했을 때 기존 버퍼가 유지된다는 전제는 보통 new_size != 0인 실패 경로에서 읽어야 합니다.
// ❌ 실패 시 원본 포인터를 잃기 쉬움
buf = realloc(buf, 100 * sizeof(int));
// ✅ 항상 임시 변수에 받기
int *tmp = realloc(buf, 100 * sizeof(int));
if (tmp == NULL) {
/* buf는 아직 유효 */
}
buf = tmp;참고 링크
2 sources