안녕하세요. 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>
함수의 이름은 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(dest, origin, 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 함수를 통해서 dest3에 origin을 복사하게 되면, 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'를 넣어주어야 하는지 살펴보아야 한다.
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)
'<개인공부> > [C언어, C++]' 카테고리의 다른 글
[C언어/C++] strlen 함수(문자열 길이)에 대해서 (1) | 2019.12.06 |
---|---|
[C언어/C++] isdigit (숫자를 판단하는 함수) (2) | 2019.11.12 |
[C언어/C++] strstr 문자열 탐색 함수에 대해서 (3) | 2019.06.04 |
[C언어/C++] strcat, strncat 문자열 연결 함수에 대해서 (5) | 2019.05.24 |
[C언어/C++] gets, puts 문자열 입출력 함수에 대해서. (4) | 2019.04.15 |
[C언어/C++] getchar,putchar 문자 입출력 함수에 대해서. (1) | 2019.04.04 |
[C언어/C++] 팩토리얼 재귀, 반복문 구하기 (factorial 함수) (2) | 2019.04.02 |
[C언어/C++] 로그함수(log, log10) 대해서. (9) | 2019.03.22 |