본문 바로가기

문자열 길이 측정, 복사, 연결 함수

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