Quick Reference
calloc은 새 저장 공간의 모든 바이트를 0으로 채우고, realloc은 이미 소유한 저장 공간의 크기를 바꿉니다. 둘 다 크기 계산, 실패, 소유권을 함께 확인해야 합니다.
int *counts = calloc(count, sizeof(*counts));
if (counts == NULL && count != 0) {
/* 할당 실패 */
}
size_t new_count = count * 2;
int *grown = realloc(counts, new_count * sizeof *counts);
if (grown != NULL) {
counts = grown; /* 성공한 뒤에만 소유 포인터를 교체 */
}
free(counts); /* 크기를 0으로 만들 때는 직접 해제 */calloc은 모든 바이트를 0으로 채울 뿐 모든 타입의 논리적 초기화를 보장하지는 않습니다. realloc 실패 시 기존 counts는 그대로 살아 있으므로, 반환값을 임시 포인터로 받아야 합니다.
calloc과 크기 계산
calloc(count, sizeof(*ptr))는 count개 원소를 위한 저장 공간을 얻고 모든 바이트를 0으로 채웁니다. 정수 카운터처럼 byte-zero가 논리적 0과 맞는 배열에서 특히 읽기 쉽습니다.
아래 함수는 새 allocation 전용입니다. 호출자는 out이 가리키는 포인터를 NULL로 시작해야 하고, 성공할 때만 새 주소를 받습니다.
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
bool make_zeroed_ints(size_t count, int out) {
if (out == NULL || *out != NULL ||
count > SIZE_MAX / sizeof(out)) {
return false;
}
int *data = calloc(count, sizeof(*data));
if (data == NULL && count != 0) {
return false;
}
*out = data;
return true;
}calloc의 표준 계약은 모든 바이트를 0으로 채운다는 것입니다. 포인터 배열의 NULL, 부동소수점 0처럼 타입의 논리적 초기값까지 모든 환경에서 보장하는 생성자라고 읽으면 안 됩니다. 그런 값이 필요하면 할당 뒤 원소 타입으로 직접 대입합니다.
count * sizeof(*ptr)는 overflow할 수 있으므로, 외부 크기에는 나누기 검사처럼 곱셈 전 검사를 둡니다.
realloc과 소유권
realloc은 성공하면 기존 블록을 같은 위치에 유지하거나 다른 위치로 옮길 수 있습니다. 실패하면 기존 allocation은 그대로 남으므로, 반환값을 임시 포인터에 받아야 합니다.
bool grow_ints(int data, size_t old_count, size_t new_count) {
if (data == NULL || new_count > SIZE_MAX / sizeof(data)) {
return false;
}
if (new_count == 0) {
free(*data);
*data = NULL;
return true;
}
if (new_count < old_count) {
return false;
}
int *tmp = realloc(*data, new_count * sizeof(*tmp));
if (tmp == NULL) {
return false;
}
*data = tmp;
for (size_t i = old_count; i < new_count; i++) {
(*data)[i] = 0;
}
return true;
}성공 뒤에도 새로 늘어난 구간은 초기화되지 않습니다. 필요한 초기값이 있으면 늘어난 구간만 직접 채웁니다. 이 함수는 성장만 담당하므로 new_count < old_count이면 실패하게 하고, 줄이는 기능은 별도 계약으로 둡니다.
주의할 점
ptr = realloc(ptr, size)는 실패했을 때 기존 포인터를 잃을 수 있습니다.realloc이 성공해 블록을 옮기면 이전 주소를 저장한 다른 포인터 복사본(alias)은 더 이상 사용하면 안 됩니다.calloc(0, size)와malloc(0)의 반환값은 구현마다 다를 수 있습니다. 반환값을 역참조하지 말고, 빈 배열 정책을 API에서 정합니다.- 크기를 0으로 줄일 때는
free와 NULL 대입을 명시적으로 수행하면 수명 규칙이 분명합니다.
참고 링크
3 sources