1. 문자열의 길이를 측정하는 함수
#include <string.h>
size_t strlen(const char * s);
매개변수로 받은 문자열 s 의 길이를 size_t 형으로 반환한다. 문자열의 길이이므로 널 문자는 제외한 길이를 반환한다. 주의할 점은 stdio.h가 아닌 string.h에 선언되어 있으므로
#include <string.h> 를 코드 앞 부분에 작성해야 한다는 것이다. .h 파일은 헤더 파일(header file)의 확장자명으로, 함수나 변수, 상수들이 선언되어 있는 파일이다.
size_t 는 보통 다음과 같이 선언되어 unsigned int 를 의미한다.
typedef unsigned int size_t;
아직은 typedef 라는 선언을 통해 unsigned int 자료형을 unsigned int 라는 이름 대신 size_t 라는 이름으로 쓸 수 있게 된다고만 알아두면 된다.
즉, unsigned int len; 과 size_t len; 은 완전히 같은 선언이다.
따라서 strlen 함수로 알아낸 문자열의 길이는 %d 가 아닌 %u로 출력하는 것이 정확하다.
하지만 문자열이 아무리 길어도 2147483647글자를 넘진 않을 것이다. 따라서 int형 변수에 저장해 %d로 출력하는 게 일반적이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <stdio.h>
#include <string.h>
int main()
{
int len;
char str[100];
printf("문자열 입력: ");
fgets(str, sizeof(str), stdin);
len = strlen(str);
printf("길이: %d, 입력된 문자열: %s", len, str);
str[len - 1] = '\0';
len = strlen(str);
printf("길이: %d, 입력된 문자열: %s", len, str);
puts("개행이 없어졌나?");
return 0;
}
|
cs |
개행이 없어졌다. 정확히 말하면 널 문자로 치환됐다. 개행 문자는 없어진 셈.
만약 개행 문자가 치환되지 않았다면 다음과 같이 출력되었을 것이다.
문자열 입력: qwerty
길이: 7, 입력된 문자열: qwerty
길이: 6, 입력된 문자열: qwerty
개행이 없어졌나?
이처럼 개행문자까지 읽어들이는 fgets 함수의 단점을 보완할 때에도 쓰이고,
이외에도 다양한 경우에 쓰인다.
2. 문자열 복사 함수
#include <string.h>
char * strcpy(char * dest, const char * src);
char * strncpy(char * dest, const char * src, size_t n);
src 를 dest 로 복사한다. 이 때 dest 에 있던 문자는 src 의 문자들로 가려진다.
예를 들어 다음의 경우 dest 에는 XYZDE 가 저장된다.
char src[4] = "XYZ";
char dest[6] = "ABCDE";
strcpy(dest, src);
이 때 strcpy 함수에서 dest 의 크기는 src 의 문자열 길이보다 커야 한다.
때문에 gets 함수와 같은 문제점이 발생한다. dest 보다 훨씬 큰 문자열을 복사할 경우 버퍼 오버플로우라는 해킹 기법에 대한 취약점이 되는 것이다.
따라서 strcpy 함수보다는 다음과 같이 n 바이트만큼만 복사하는 strncpy 함수를 사용하는 것이 바람직하다.
또 한 가지 주의할 점은 널 문자다. 다음과 같이 복사한다고 하자.
char src[30] = "ABCDEFGHIJKLMNOPQRSTUCWXYZ";
char dest[10];
strncpy(dest, src, sizeof(dest));
이 경우 ABCDEFGHIJ 가 복사되기 때문에, dest 의 끝에는 널 문자가 아닌 J 가 저장된다.
따라서 이 경우 sizeof 연산자나 strlen 함수 등을 이용해 널 문자를 넣어줘야 한다. 그렇지 않으면 이상한 문자가 출력된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#include <stdio.h>
#include <string.h>
int main()
{
char src[30] = "ABCDEFGHIJKLMNOPQRSTUCWXYZ";
char dest1[100];
char dest2[30];
char dest3[10];
puts("====strncpy(dest1, src, sizeof(dest1))====");
strncpy(dest1, src, sizeof(dest1));
fputs("src: ", stdout); puts(src);
fputs("dest1: ", stdout); puts(dest1);
puts("\n====strncpy(dest2, src, sizeof(dest2))====");
strncpy(dest2, src, sizeof(dest2));
fputs("src: ", stdout); puts(src);
fputs("dest2: ", stdout); puts(dest2);
puts("\n====strncpy(dest3, src, sizeof(dest3))====");
strncpy(dest3, src, sizeof(dest3));
dest3[sizeof(dest3) - 1] = '\0';
fputs("src: ", stdout); puts(src);
fputs("dest3: ", stdout); puts(dest3);
return 0;
}
|
cs |
dest3 의 경우 널 문자 확보를 위해 ABCDEFGHIJ 를 복사한 후 J 를 널 문자로 치환했다.
꼭 이 방법만 있는 것은 아니다. dest3 의 모든 요소를 0으로 초기화한 후
sizeof(dest3) - 1 만큼 복사하는 방법도 있다.
참고로 dest3의 끝에 널 문자를 넣지 않으면 다음과 같이 출력된다.
항상 저 한자만 출력돼서 찾아봤는데, 경계할 경이라는 한자였다.
왜 하필 이 한자인지는 모르겠다. 추측만 해보고 있다. 또한 현재로서는 TMI이므로 스킵한다.
3. 문자열 연결 함수
#include <string.h>
char * strcat(char * dest, char * src);
char * strncat(char * dest, char * src, size_t n);
dest 의 뒤에 src 를 덧붙인다. 이 때 dest 의 널 문자 다음이 아닌, 널 문자가 있는 곳부터 덧붙여진다.
따라서 다음과 같이 호출할 경우,
char src[4] = "DEF";
char dest[10] = "ABC";
strcat(dest, src);
다음과 같이 덧붙여진다.
다만 strcpy 함수와 같은 이유로 다음과 같이 src 의 n 바이트만큼만 덧붙이는 strncat 함수를 사용하는 것이 바람직하다.
char src[10] = "DEF";
char dest[10] = "ABC";
strncat(dest, src, strlen(src));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <stdio.h>
#include <string.h>
int main()
{
char src[10] = "DEF";
char dest[10] = "ABC";
fputs("src: ", stdout);
puts(src);
fputs("dest: ", stdout);
puts(dest);
strncat(dest, src, strlen(src));
fputs("dest: ", stdout);
puts(dest);
return 0;
}
|
cs |
4. 보안
앞에서 보안 상의 문제를 계속 언급했는데, 사실 strncpy 함수와 strncat 함수라고 해서 완전히 안전하지는 할 수 없다. 실제로 Visual Studio 에서는 strcpy_s, strncpy_s, strcat_s, strncat_s 함수만 사용하게 하고 있다. scanf 함수의 경우도 scanf_s 함수만 사용하게 하고 있다.
scanf_s 함수의 경우 %s 로 문자열을 입력받을 때 문자열을 입력받을 문자열의 길이까지 매개변수로 주도록 하고 있다.
다만 여기서는 함수 원형을 소개해야 하기 때문에, '상대적으로' 더 안전한 strncpy, strncat 함수의 사용법만을 소개하는 것이다.
'Programming > C' 카테고리의 다른 글
sprinf, sscanf 함수 (0) | 2019.05.18 |
---|---|
문자열 비교 함수 (0) | 2019.05.14 |
문자열 입출력 함수 (0) | 2019.05.07 |
문자 입출력 함수 (0) | 2019.05.05 |
스트림 (0) | 2019.05.01 |