
strcpy vs strncpy 차이는 C 문자열 함수를 배울 때 가장 많이 오해하는 주제 중 하나입니다. 이름만 보면 strncpy가 단순히 더 안전한 버전처럼 느껴지지만, 실제로는 널 종료 보장과 패딩 규칙이 달라서 오히려 다른 종류의 실수를 만들 수 있습니다.
이 글에서는 어떤 함수가 무조건 더 낫다고 단정하지 않고, 두 함수가 각각 무엇을 보장하고 무엇을 보장하지 않는지 차분하게 정리하겠습니다.

먼저 결론부터: 둘의 차이는 어디에 있을까
- strcpy는 src 전체를 널 문자까지 복사한다.
- strncpy는 최대 count개만 복사한다.
- strncpy는 잘리면 결과가 널 종료되지 않을 수 있다.
- strncpy는 src가 짧으면 남는 칸을 널 문자로 패딩한다.
즉 strncpy는 자동으로 안전한 문자열을 만들어 주는 함수가 아닙니다. 이 한 줄을 먼저 잡고 가면 뒤의 설명이 훨씬 쉬워집니다.
strcpy는 정확히 무엇을 할까
char src[] = "abc";
char dest[4];
strcpy(dest, src); // a, b, c, \0strcpy는 src가 가리키는 널 종료 문자열을 마지막 \0까지 복사합니다. 그래서 정상적으로 끝나면 dest도 문자열이 됩니다.
문제는 함수가 dest 크기를 모른다는 점입니다. 그래서 목적지 버퍼가 충분히 크다는 전제가 없으면 바로 undefined behavior 위험으로 이어집니다.
char src[] = "abcdef";
char dest[4];
strcpy(dest, src); // 정의되지 않은 동작Linux strcpy(3) man page와 cppreference strcpy 문서도 dest가 충분히 크지 않으면 동작이 정의되지 않는다고 설명합니다.
strncpy는 왜 더 안전해 보이지만 오해를 부를까
strncpy(dest, src, count)는 최대 count개 문자만 복사합니다. 여기까지만 보면 길이 제한이 있으니 더 안전해 보입니다.
하지만 count 안에서 널 문자를 만나지 못하면 결과 배열은 문자열이 아닐 수 있습니다. 반대로 src가 더 짧으면 남는 count 구간을 계속 \0로 채웁니다.

char dest[4];
strncpy(dest, "abcdef", sizeof(dest));
printf("%s\n", dest); // 위험이 예시의 dest에는 a b c d만 들어가고 널 문자는 없습니다. 이 상태에서 printf("%s")나 strlen에 넘기면 정의되지 않은 동작이 될 수 있습니다.
CERT STR32-C도 바로 이런 패턴을 경고합니다.
src가 짧을 때는 왜 또 성격이 달라질까
char dest[8];
strncpy(dest, "hi", sizeof(dest));
// h i \0 \0 \0 \0 \0 \0strncpy는 문자열이 일찍 끝나면 나머지 칸을 모두 널 문자로 패딩합니다. 그래서 단순히 길이를 제한한 복사라고만 이해하면 실제 동작을 놓치게 됩니다.
이 규칙은 고정 길이 필드를 다룰 때는 의미가 있지만, 일반적인 문자열 복사에서는 의도보다 무거울 수 있고 코드 독자도 쉽게 헷갈립니다.
그러면 어느 쪽이 더 안전할까
정답은 함수 이름이 아니라 정책입니다. 버퍼가 충분히 큰 것이 확실하고, 잘림을 허용하지 않으며, 전체 복사가 목적이라면 strcpy의 전제가 더 단순할 수 있습니다.
반대로 길이를 제한하고 싶다면 strncpy를 쓸 수는 있지만, 결과가 문자열인지 직접 보장하는 후처리가 빠지면 안 됩니다.
안전성은 strncpy라는 이름에서 자동으로 오지 않습니다. 널 종료 보장을 코드에서 직접 처리해야 합니다.
truncation을 허용한다면 이런 패턴이 더 명확하다
enum { DEST_SIZE = 8 };
void copy_text(char dest[DEST_SIZE], const char *src)
{
strncpy(dest, src, DEST_SIZE - 1);
dest[DEST_SIZE - 1] = '\0';
}이 패턴의 핵심은 마지막 칸을 직접 \0로 고정하는 것입니다. 그러면 잘렸더라도 최소한 dest는 문자열이 됩니다.
다만 잘림이 발생했는지 별도로 알기 어렵다는 한계는 남습니다. 잘림이 에러여야 하는 상황이라면, 먼저 들어갈 수 있는지 검사한 뒤 복사하는 방식이 더 분명합니다.
실무에서 기억하면 좋은 체크리스트
- src가 정말 널 종료 문자열인가
- dest가 충분히 큰가
- 잘림을 허용하는가
- 결과를 문자열 함수에 넘길 것인가
- 널 종료를 코드에서 직접 보장했는가
관련해서 문자열과 배열 감각을 먼저 정리하고 싶다면 C 언어 문자열과 문자 배열 차이 글이 도움이 됩니다. 배열과 포인터의 정체성 차이까지 함께 보고 싶다면 C 언어 포인터와 배열 차이 글도 이어서 보면 흐름이 자연스럽습니다.
외부 기준은 cppreference strcpy, cppreference strncpy, Linux strcpy(3) man page, CERT STR32-C를 참고했습니다.