<개인공부>/[C언어, C++]

[C언어/C++] strcpy, strncpy 함수(문자열 복사)에 대해서

BlockDMask 2019. 5. 16. 03:47
반응형

안녕하세요. BlockDMask 입니다.

오늘은 C 스타일의 문자열인 char*, char[] 타입의 문자열복사하는 함수 두가지에 대해서 알아 볼 것 입니다.

두 함수는 바로 strcpy, strncpy 입니다.

이 두함수가 무슨 기능을 하는지, 어떤 헤더파일을 가지고 있는지, 매개변수는 무엇인지, 어떤식으로 사용하는지 한번 알아보도록 하겠습니다.


C++ string 클래스에 대해서 알고싶다면  [바로가기]

C/C++ 문자열 이어붙이기 strcat [바로가기]

C/C++ 문자 입출력 함수 getchar,putchar [바로가기]

C/C++ 문자열 입출력 함수 puts, gets  [바로가기]


<목차>

1. strcpy, strncpy 함수에 대해서.

2. strcpy, strncpy 함수 사용 예시.

3. strcpy, strncpy 함수 사용시 제일 주의해야할 것. (진짜 레알 필독)


1. strcpy, strncpy 함수에 대해서.

strcpy 함수란?

헤더파일 : <string.h>     //C++에서는 <cstring>

함수원형 : char* strcpy(char* dest, const char* origin);

함수의 이름은 str (=string), cpy (=copy) 문자열을 복사하는 함수 입니다.

: origin에 있는 문자열 전체를 dest로 복사를 하는 함수 입니다.


> 간단한 사용법.

char origin[] = "BlockDMask";

char dest[100];

strcpy(dest, origin);



strncpy 함수란?

헤더파일 : <string.h>     //C++에서는 <cstring>

함수원형 : char* strncpy(char* dest, const char* origin, size_t n);

str과 cpy 사이에 n 이 있죠? 이게 number를 뜻하는 것 입니다.

그러므로, str(=string), n(=number), cpy(=copy) 을 뜻하게 됩니다.

: origin에 있는 문자열을 dest로 복사를 하는데, n 만큼만 복사하는 함수 입니다.


**다시 말씀드리지만 맨 앞에 있는 문자열이 destination. 즉, 복사를 당할 문자열 입니다.


> 간단한 사용법.

char origin[] = "BlockDMask";

char dest[100];

strncpy(destorigin, sizeof(origin));


2. strcpy, strncpy 함수 사용 예시.


 strcpy 함수예시 (복사받을 배열의 길이가 충분할때와 그렇지 않을때)

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
//[C언어/C++] strcpy example. :BlockDMask:
#include<stdio.h>    //C++ <iostream> or <cstdio>
#include<string.h>    //C++ <cstring>
 
int main(void)
{
    char origin[] = "BlockDMask";    //"BlockDMask\0" 이므로 size = 11;
    char dest1[20];
    char dest2[10];
    char dest3[] = "STRCPY_EXAMPLE";    // size = 15;
 
    //case1 : 빈 배열에 전체를 복사할때
    strcpy(dest1, origin);
 
    //case2 : 꽉 차있는 배열에 전체를 복사할때
    //strcpy(dest2, origin);    //run time error
 
    //case3 : 꽉 차있는 배열에 전체를 복사할때
    strcpy(dest3, origin);
 
    printf("case1 : %s\n", dest1);    //ok
    //printf("case2 : %s\n", dest2); //run time error
    printf("case3 : %s\n", dest3);    //ok
    return 0;
}
 
cs


→ 결과

: 1번 케이스를 보면 비어있는 배열에 origin 문자열이 잘 복사 된것을 확인할 수 있습니다.

: 3번 케이스를 보면 꽉 찬 배열에 origin 문자열을 복사하면 기존것은 안나오고 orgin만 나오는 것을 확인 할 수 있습니다.

오잉? 3번 케이스가 좀 의아하지 않나요?

문자열 dest3이 origin보다 더 긴데, 복사해서 넣으면 왜 origin만 나오는거죠?

그것에 대한 해답은 아래 3.strcpy 주의사항에 있습니다.


 

 strncpy 함수예시 (전체 복사, 일부복사, 배열이 차있을때, 배열이 비어있을때)

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
//[C언어/C++] strncpy example. :BlockDMask:
#include<stdio.h>    //C++ <iostream> or <cstdio>
#include<string.h>    //C++ <cstring>
int main(void)
{
    char origin[] = "BlockDMask";    //"BlockDMask\0" 이므로 size = 11;
    char dest1[20];
    char dest2[] = "abcdefghijklmnop";    //size = 17;
    char dest3[] = "STRNCPY_EXAMPLE";    //size = 16;
    char dest4[10];
 
    //case1 : 빈 배열에 전체를 복사할때
    strncpy(dest1, origin, sizeof(origin));
 
    //case2 : 꽉 차있는 배열에 전체를 복사할때
    strncpy(dest2, origin, sizeof(origin));
 
    //case3 : 꽉 차있는 배열에 일부만 복사할때
    strncpy(dest3, origin, 4);    // 오!
 
    //case4 : 빈 배열에 일부만 복사할때
    strncpy(dest4, origin, 4);    // 그럼이건?
 
    printf("case1 : %s\n", dest1);
    printf("case2 : %s\n", dest2);
    printf("case3 : %s\n", dest3);
    printf("case4 : %s\n", dest4);
    return 0;
}
cs


→ 결과

1번 케이스 : 비어있는 배열에 origin 문자열의 전체가 잘 복사 된 것을 확인할 수 있습니다.

2번 케이스 : 꽉 차있는 베열에 origin 문자열의 전체를 복사했는데, 분명히 dest2가 더 긴데 왜 origin만 나오죠?

3번 케이스 : origin의 4번째 까지 Bloc를 잘 복사해서 dest3에 붙여넣은것을 알 수 있습니다.

4번 케이스 : 빈 배열에 일부 문자열만 복사했는데, 왜 쓰레기 값들이 나올까요?


2번과 4번 케이스가 궁금하시다구요? 그럼 아래 strncpy 주의사항으로 GOGO!


3. strcpy, strncpy 함수 사용시 제일 주의 해야할것. (필독)


strcpy는 문자열 끝(= '\0') 까지 복사를 한다.

char*, char[] 타입의 문자열 끝에는 '\0' 이 있는것 아시죠? 이걸로 문자열의 끝을 판단하게 되잖아요.

strcpy로 복사를 하게 되면 문자열의 끝을 나타내는 '\0' 까지 복사가 됩니다. (그게 뭐 뭔상관인데)


strcpy의 case3번을 보시고 약간 의아하지 않으셨나요?

위에서 제가 보여드렸던 예제 중에서 case3번을 보겠습니다.

1
2
3
4
5
6
7
char origin[] = "BlockDMask";    //"BlockDMask\0" 이므로 size = 11;
char dest3[] = "STRCPY_EXAMPLE";    // size = 15;
 
//case3 : 꽉 차있는 배열에 전체를 복사할때
strcpy(dest3, origin);
 
printf("case3 : %s\n", dest3);    //ok
cs



여기서 보면, dest3이 origin보다 길이가 더 긴 문자열입니다. 

보통 저렇게 복사하면 앞에 문자열이 더 작으니까 둘이 붙어서 "BlockDMaskMPLE"가 나와야하는거 아닌가? 하는 생각이 들 수 있습니다.


[정리]

strcpy 함수를 통해서 dest3origin을 복사하게 되면, dest3은 메모리 상에서 아래와 같이 될 것 입니다.

"BlockDMask\0PLE\0" 이런 형식이 됩니다.

 strcpy를 통해서 배열의 끝인 \0까지 복사가 되었기 때문에, dest3 문자열의 10번째 인덱스에 '\0' 존재하게 됩니다.

그러므로 dest3의 문자열은 "BlockDMask\0" 여기까지 인식하게 되어서, 프린트를 하게 되면, BlockDMask 이런식으로 나오게 되는 것 입니다.


좀더 이해하기 쉽게 아래 예제를 준비했습니다.

1
2
3
4
5
6
7
8
char origin[] = "BlockDMask";
char dest3[] = "STRCPY_EXAMPLE";
strcpy(dest3, origin);
 
printf(" case3 before : %s\n", dest3);
    
dest3[10= 'y';    //'\0' -> 'y'
printf(" case3 after  : %s\n", dest3);
cs


 

"BlockDMask\0PLE\0" 상태의 문자열에서 처음 나타나는 '\0' 를 'y'로 바꾸어 봤습니다.

y로 바뀌니까 PLE 까지 나오는것 보이시죠?


꼭 기억해주세요. strcpy로 복사를 하면 문자열의 끝을 알리는 '\0'까지 복사가 된다.

▼ strncpy로 복사했을경우 적절한 위치에 '\0'를 넣어주어야 하는지 살펴보아야 한다.

strncpy의 case2번과 4번에 대해서 궁금하셔서 여기까지 오셨군요.
case2, 4번 둘다 문자열의 끝을 가리키는 '\0' 때문에 발생하게 되었습니다.
strcpy와 달리 strncpy는 기본적으로 '\0'를 상관하지 않고 n의 길이만큼만 딱 복사합니다.

char origin[] = "blockdmask";
그게 무슨말인고 하면,
strncpy(dest2, origin, sizeof(origin)) 을 하게되면 blockdmask\0 이므로 \0 까지 복사가 될 것이고
strncpy(dest2, origin, 4) 을 하게되면 bloc 이므로 \0 없이 복사가 될 것입니다.

[case2번 정리]
1
2
3
4
5
6
7
8
9
char origin[] = "BlockDMask";
char dest2_1[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
char dest2_2[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
    
strncpy(dest2_1, origin, sizeof(origin));
strncpy(dest2_2, origin, sizeof(origin) - 1);
 
printf(" case2_1 : %s\n", dest2_1);
printf(" case2_2 : %s\n", dest2_2);
cs


: case2_2 처럼 문자열을 연결하고 싶다면, sizeof(origin) - 1을 하여서 문자열의 끝에있는 '\0' 이것을 빼고 복사 하면 됩니다.


[case4번 정리]

1
2
3
4
5
6
7
8
9
10
11
char origin[] = "BlockDMask";
char dest4_1[20];
char dest4_2[20];
 
strncpy(dest4_1, origin, 4);
strncpy(dest4_2, origin, 4);
 
printf(" case4_1 : %s\n", dest4_1);
 
dest4_2[4= '\0';
printf(" case4_2 : %s\n", dest4_2);
cs


: case4_2 처럼 문자열을 복사한 다음에, 문자열 끝을 알리는 '\0'을 적절한 위치에 넣어주면 됩니다. (dest4_2[4] = '\0')

: case4_1 의 경우에는 printf를 통해서 문자열을 출력하려 하는데 '\0'이 보이지 않아서, 순서대로 메모리를 쭉 훑다가 origin에 있는 BlockDMask\0 을 발견하고 출력한 것입니다. 당연하게도 메모리 중간중간에 비어있는 곳은 쓰레기 값이 출력 되겠죠?


▼ strncpy로 복사했을경우 n의 길이를 주의해야 한다.

strncpy(dest, origin, n)

n의 크기 sizeof(origin)보다 작거나 같아야 합니다. (휴먼에러 발생할 수 있음)

또한, dest의 길이보다 n은 작거나 같아야합니다. (런타임 에러)


1. n <= sizeof(origin)

2. n <= sizeof(dest)

반응형