본문 바로가기

C_Data Structure_Algorithm/C String

2) 문자열 기본 함수

* 문자열 기본 함수


# include <string.h > strcpy : 문자열 복사 strlen : 문자열 길이 strcat : 문자열 합치기 strcmp : 문자열 비교

# 예제 1.


출력결과 : helloR?

즉, hello 가 출력이 되어야 하는데, 뒤에 이상한 애들이 붙어서 출력되는 것이다.

왜? NULL char 의 문제.

다시 말하면, 현재 우리는 word[10] 을 통해서,

10칸의 공간을 할당했다.

그런데 우리는 word[ 0 ] 부터, word[ 4] 까지의 공간만을 할당했다. 즉, hello 라는 5개의 문자만 저장했다.

이때 우리는 word를 출력하는데, 함수 입장에서는

어디까지 출력해야 할지를 모르는 것이다.

즉, word[5] = '\0'

이런식으로 null char 을 넣어줘야 하는 것이다.

#예제 2.


words[0]

여러개의 단어들을 사용자로부터 입력받아서,

입력받은 단어들을 프로그램 안에 저장하는 것.

그 단어들을 어떻게 저장할 것인가.?

단어가 여러개 이니까

당연히 데이터가 하나가 아니라, 여러 개일 경우,

그 데이터를 배열에 저장하는 것이 가장 기본적인 방법.

단어가 여러개니까,

단어들을 저장하는 배열을 만들어서,

배열의 각 칸에 단어들을 넣는 것인데,

하나의 단어는 sky\0 와 같이, 문자들의 배열로 저장이 되는데, 이런 단어들이 저마다 길이도 다른 , 이런 단어들이

여러개 들어와서, 그런 여러개 단어들이 저 배열에 저장되는 것이다.

즉, 하나의 배열( words 라고 이름을 붙이자 ) 이 있고

각 칸에 단어가 저장이 되는데,

그 단어 자체가 다시 문자배열.

#define BUFFER_SIZE 100 int main(){ char * words[100]; int n = 0 ; // 현재까지 입력받은 문자의 갯수. // C언어에서 배열을 선언할 때는, 배열의 이름과, 크기와 , type을 지정한다. // 배열의 type : 배열의 각각의 칸에 저장되는 배열의 type 을 말한다. // words[1] 에 저장되는 데이터의 타입은 ? cloud\\0 라는 배열의 '주소' . // 즉, 포인터 변수. 이로 인해서 * words 가 되는 것. // 포인터 변수인 것은 알겠어. 그렇다면, 무슨 포인터 변수야 ? // words[1] 가 어떤 주소를 저장하는데, 그 주소에 갔을 때, 그 주소에 저장되는 데이터의 type 이 뭐냐 // words[1] 에는, cloud\\0 의 첫번째 칸의 주소가 저장된다. // 그리고 cloud\\0 라는 배열의 첫번째 칸에는 'c' 라는 문자가 저장이 된다. // 그렇기 때문에 char * 타입이 되는 것이다. // 이제 돌아가면서, 문자열을 입력받을 것이다 // 일반적으로 문자열을 입력받을 때 쓰는 함수는 scanf 함수이고, // 입력받은 문자열을 저장하는 배열을 지정해야 한다. // 그것을 buffer[ BUFFER_SIZE ] 라고 지정해준 것이고, BUFFER_SIZE 를 // 100 으로 지정한 것은, 문자열 하나의 길이가 100이 넘지 않을 것이라고 한 것. char buffer[BUFFER_SIZE]; // 우리가 일반적으로 scanf( "%s", &a ) 이런식으로, 주소값을 넘겨준다. // 그런데, 왜 아래에는 buffer 만, 즉, 왜 & 를 생략하는 것일까 ? // 위에서도 말했듯이, buffer 는 배열의 이름이고, 배열의 이름은, 배열의 시작주소를 갖고 있기 때문이다. // 즉, 배열의 이름은 그 배열의 주소를 저장하고 있는 포인터 변수이므로. // ! = EOF : 파일의 끝에 도달하기 전까지. 반복하기. while( scanf( "%s" , buffer )! = EOF ) { // 후보 1) // words[n] = strcpy( words[n] , buffer ) ; // 아래, "오류1"에 strcpy 를 쓰는 이유가 나와있다. // strcpy : 문자열을 복사하는 것. 2개의 매개변수 필요 // strcpy ( array1 , array 2 ) // arrary 2 를 array1 에 복사한다. 즉, 뒤에것을 앞으로. // 현재 buffer 에 저장되어 있는 문자열을 , words[ n ] 에 저장해라. // words[n] 이라고 쓰면 안된다. 왜 ? // array1 자리에 들어갈 것은 문자열 배열이어야 한다. // 그런데, words[ n ] 은 포인터 변수일 뿐이지, words[n] 자체가 문자 배열은 아니다 // 어떤 문자 배열의 주소를 저장할 포인터 변수일 뿐. // 그렇다면 ... 어떻게 하지 ? strcpy 상으로 할 수 있어? // 아니, 그래서 쓰는 것이 strdup words[n] = strdup(buffer) ; // strdup 는, 매개변수로 하나의 문자열을 받아서 , 그것의 복제본을 만들고 // 그 복제본의 주소를 반환하는 역할을 하는 것 : strdup // 그래서 그 주소를 words[n] 에 넣는 것이다. n++ ; // 이런식으로 n 을 증가시켜 주면, ex. sky\\0 은 words[] 라는 배열의 첫번째 칸에 // clouds\\0 라는 애는, 배열의 2번째 칸에 저장되는 것이다. }

#예제2_1. " words[n] = buffer "


이 경우, 가장 마지막에 있는 단어만 4번 출력되고,

우리가 입력한 위의 3개 문자는 사라져 버렸다. 왜 ?

while( scanf( "%s" , buffer )! = EOF ) { words[n] = buffer ; n++ ; }

사용자가 입력한 문자열을 buffer 라는 것에 저장했고,

words[ n ] 이라는 칸에 현재 저장을 시키는 것인데,

words[ n ] 에 저장되는 것은 char * 타입이다.

buffer 라는 것은 문자배열의 이름이다.

buffer 도 buffer[100] 크기의 문자배열의 '시작주소'를 가리키는 포인터 변수이다.

즉, words[ n ] = buffer 는

2개의 포인터 변수들 간에 치환문 이다.

포인터 변수들 간에 치환을 하게 되면, 치환문의 의미는

항상 같다. = 의 오른쪽을, 왼쪽에 넣는 것이다.

즉, buffer 라는 애가 가진 값을 words[n] 이라는

포인터 변수에 저장하는 것이다.

이 그림에서 buffer 를 위한 공간이 하나 별도로 있다고 생각해라.

만일 while 문을 통해서 2번째 문자열을 입력받게 되면,

기존에 입력받았던 문자열이 지워지고, 그 위에 새로운 문자열이 들어갈 것이다.

그 다음에 다시 배열의 2번째 칸에 buffer 의 값이 아니라,

words[n] = buffer, 즉, buffer 의 주소를 넣으면,

words[n] 의 각각의 칸마다,

sky\0 , cloud\0 , rain\0 이렇게 각각의 값이 별도로 들어가는 것이 아니라

words[ 0 ~ n] , 즉, 각각의 칸은, buffer 의 '주소'가 들어가는 것이다.

즉, words[ ] 의 모든 칸이 buffer 라는 하나의 배열의 주소로 들어가는 것이다.

이 buffer 라는 배열의 값은, 사용자가 새롭게 입력을 할때마다 새로운 값으로 바뀌게 된다.

그 이후 words[ n ] 에 저장된 , 각각의 문자열을 출력해보면, buffer 라는 하나의 배열 안에, 저장되어 있는 문자열이 4번 연속되어 출력되는 것이다.

그리고 처음 입력했던 3개의 문자열은 당연히 지워진다. 가장 마지막에 입력했던 문자열만 저장되어 있을 것이다.

해결책은 , buffer 의 '값'을 매번 words [ n ] 에 넣는 것이다.

#예제2_2. strdup 함수



이는 strdup 의 작동원리라고 할 수 있다.

malloc 을 이용하여, 새로운 배열을 할당받는다.

strdup 가 하는 일은, 입력으로 하나의 문자열 s 를 받아서, s 가 하나의 문자열이니까, s 는 문자배열의 시작 주소를 저장하는 포인터 이다.

그 다음, malloc 으로, 동적 메모리 할당으로,

strlen(s) + 1 을 받는다.

s > sky\0 의 경우,

strlen( s ) 는 3이 된다. 거기에 \0 까지 포함되어야 하니까 +1 을 하는 것이다. 그래서, 크기가 4인 새로운 배열을 할당 받아서, 그것을 일단 p 라고 한다.

p = ( char * ) malloc( strlen( s ) + 1)

즉, p 에 우리가 할당하는 새로운 배열의 시작 주소를 넣는 것이다.

if( p ! = NULL )

strcpy( p , s ) ;

배열 s 에 저장되어 있는 문자열을 배열 p 로 , copy를 한 다음에,

새로 만들어진, 배열의 주소 p 를 리턴하는 것이다.

그래서 우리는

words[ n ] = strdup( buffer )에서,

strdup 가 반환해주는 주소를 받아서

word 에 넣는 것이다.

즉, 정리를 하면, 이 공간에 별개인 buffer 라는 애가 있다.

strdup 를 하면, 이 공간에서 buffer 라는 애와 별개의 공간이 만들어진다.

더구나 길이도 ex. sky\0 와 같이 , 단어 길이와 딱 맞는 문자배열이 만들어지면, 그것을 words[n]에 딱 달고,

다시 while 문에 들어가서, buffer 에 clouds\0 라는 값이 입력이 되면, 그것을 strdup 하면, 복제한, 정확히 길이가 cloud\0 와 맞는, 문자배열이 만들어지고 , 그것을 words[1] 에 넣기. 이러한 방식을 반복한다.