목차
1. sprintf() vs snprintf()
2. 응용/심화
3. 요약
1. sprintf() vs snprintf()
sprintf :
sprintf 함수는 문자열을 생성하는 함수.printf()와 유사하지만 출력 대신 문자열을 버퍼에 저장.
int sprintf(char str, const char format, ...);
- str : format을 복사해서 저장할 곳.
- format : 서식지정자가 포함된 문자열
- 반환 : 생성에 성공한 문자의 개수. (실패 -1)
- 배열 크기를 넘어서는 문자열을 저장하면 버퍼 오버플로우 발생.
#include <stdio.h>
void MyFunc() {
char buff[30];
int n1 = 1;
int n2 = 2;
sprintf(buff, "n1 = %d, n2 = %d", n1, n2);
printf("%s", buff);
return 0;
}
// n1 = 1, n2 = 2 출력
snprintf :
sprintf() 함수와 기능적으로 동일하나 버퍼 크기를 인자로 받아 버퍼 오버플로우를 방지하는 포맷팅 함수.
리턴값으로 잘림과 에러를 구분하는 것이 가능.
-> 버퍼 크기를 초과하는 데이터가 들어오면 size - 1까지만 쓰고 마지막에는 반드시 NULL 종단 문자를 붙여서 마무리.
-> 리턴값은 버퍼 크기 제한이 없었다면 썼을 전체 길이를 반환. (실제로 쓴 길이가 아님)
-> 반환값이 size보다 크거나 같다면 데이터가 잘렸다는 의미.
int snprintf(char *str, size_t size, const char *format, ...);
char buffer[16];
int n = snprintf(buffer, sizeof(buffer), "DEV:%s", deviceName);
// 1. 에러 발생 여부 확인 (음수 리턴)
if (n < 0) {
HandleFormattingError();
}
// 2. 잘림 발생 여부 확인 (버퍼 크기보다 크거나 같음)
else if (n >= (int)sizeof(buffer)) {
// 로그가 잘렸음을 사용자나 시스템에 알림
LogWarning("Message truncated: original length was %d", n);
}
2. 응용/심화
포맷 스트링 취약점
snprintf()와 같이 외부 입력을 직접 포맷 인자로 넣는 행위는 매우 위험.
ex) sprintf(buffer, size, input);
-> input 대로 포맷을 맞춰서 출력 : input에 문자열 대신 %x혹은 %n과 같은 특수 기호를 사용하면 오동작.
-> %x는 메모리 주소를 16진수로 보여달라는 명령으로 메모리 정보를 화면에 출력하는 케이스 발생.
-> %n은 특정 메모리 주소의 값을 바꾸는 것이 가능하며 프로그램 주도권에까지 영향이 가능.
안전한 코드 :
snprintf(buffer, size, "%s", input)
- 포맷 인자를 %s로 고정하면 input에 어떠한 기호가 오더라도 문자로 인식하여 처리. (명령어로 인식되지 않음)
3. 요약
sprintf : 문자열을 버퍼에 저장하는 함수, 버퍼 사이즈 체크 하지 않음, 에러 검출 불가.
-> 안정성 낮고, C90/C99/C++에서 지원.
snprintf : 문자열을 버퍼에 저장하는 함수, 버퍼 사이즈 체크 함, 에러 검출 가능.
-> 안정성 높고, C99/C++11에서 지원.
- 프로그램 안정성을 위해서는 sprintf, strcpy와 같이 크기를 지정하지 않는 함수의 사용을 자제.
- snprintf 호출 이우에는 반드시 반환값과 버퍼 크기를 비교하여 데이터 유실 여부 판단.
'Develop > Language - C++' 카테고리의 다른 글
| [C++] 메모리 / 핸들 누수 및 추적 도구 (0) | 2026.01.25 |
|---|---|
| [C++] C++ 기호 시퀀스 (0) | 2026.01.25 |
| [C++] _wtoi, wcstoul (0) | 2026.01.23 |
| [C++] Size vs Capacity (0) | 2026.01.23 |
| [C++] size_t (0) | 2026.01.23 |