ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [C Language] 40. 문자열 함수 - C 언어
    CSE/C Language 2015. 8. 5. 13:29

    1. gets(), fgets()

     문자열을 사용자로부터 받아들일 때 가장 많이 사용하는 함수는 바로 gets()이다. 하지만 이것은 상당히 위험한 함수이며 초보자는 반드시 fgets()를 사용해야 한다. 이번 단원에서는 gets()의 위험성을 알아보고 이 대신 fgets()를 사용할 것을 권장한다.


     gets()는 리눅스 메뉴얼에 보면 사용하지 말라는 뜻에서 '저주받은 함수'라는 극한적인 말로 표현되어 있다. 얼마나 위험한지 아래 예제를 통해 알아보도록 하자.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    /*
     * gets.c
     *
     *  Created on: 2015. 8. 5.
     *      Author: Yeonsu
     */
     
    #include <stdio.h>
     
    int main(void) {
     
        char name[5];
     
        gets(name);
        printf("Your name is %s\n", name);
     
        return 0;
    }
     
    cs










     전혀 위험해 보이지 않지만 위의 프로그램은 다음과 같은 메모리 구조를 가짐으로 써 프로그램을 묵시적인 위험에 빠트리고 있다.







     gets()를 잊기 위해서는 대체 함수가 필요한데 이것이 바로 fgets() 이다. gets()는 get string의 의미가 있으며 fgets()에서의 'f'는 file을 뜻한다. 그런데 C에서의 file이라는 것은 화면, 테이프, CD 등을 포괄하기 때문에 화면을 통한 문자열 입력도 fgets()로 가능하다.




    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    /*
     * gets.c
     *
     *  Created on: 2015. 8. 5.
     *      Author: Yeonsu
     */
     
    #include <stdio.h>
     
    int main(void) {
     
        char name[5];
     
        fgets(name, sizeof(name), stdin);
     
        printf("Your name is %s\n", name);
     
        return 0;
    }
     
    cs











     

     안전하게 확보된 영역은 5 바이트이며 널 문자를 포함해야 하므로 4 문자 'Moon'이 name에 저장되고 출력되었다. 






    2. strcpy()

     


    1
    2
    3
    4
    5
    6
    7
    8
    char *imsip;
    char dim[20];
     
    strcpy(imsip, "Yeonsu Moon");
    strcpy(dim, "Yeonsu Moon");
     
    imsip = "Yeonsu Moon";
    dim = "Yeonsu Moon";
    cs



     위 코드를 보면 5번과 7번 라인은 문제가 없는 라인이고, 4번과 8번 라인은 문제가 있는 라인이다.


     4번 라인의 문제를 보기 전에 strcpy()의 원형을 보도록 하자.




    char *strcpy(char *s1, const char *s2); 


     첫 번째 인자와 두 번째 인자 모두 주소 값을 원하고 있고 리턴되는 값도 주소 값이라는 것을 알 수 있다.

     문제가 된 4번의 첫 번째 인자 imsip는 포인터 변수이기 때문에 분명히 주소 값을 가지고 있을 것이다. 또한, 두 번째 인자인 "Yeonsu Moon" 은 문자열이기 때문에 'Y'가 저장된 주소 값이 인자로 전달될 것이다. strcpy()의 원형을 해석하면 s2가 가리키는 주소 값에서 값을 취하여 s1이 가리키고 있는 주소 값에 저장하는 것이다.

     여기까지 해석해도 큰 문제가 없어 보인다. 하지만 사실 한 가지 위험요소를 놓치고 있다. strcpy()의 원형을 해석한 것을 토대로 위 코드를 그림을 통해 살펴보자.









     strcpy(imsip, "Yeonsu Moon");







     strcpy(imsip, "Yeonsu Moon")을 사용하면 첫 번째 인자의 주소와 두 번째 인자의 주소를 취한 후 두 번째 인자가 가리키는 주소에서 문자를 하나씩 imsip가 가리키고 있는 주소쪽으로 복사하기 시작한다. 조금 쉽게 설명하면 'Yeonsu Moon' 이라는 문자열을 imsip가 가리키고 있는 곳에 복사해 넣는다는 뜻이다. 그런데 문제는 imsip에 있다. imsip가 가리키고 있는 주소는 어디에서도 초기화를 해준적이 없기 때문에 쓰레기 주소 값(0x24FF)이 존재한다.쓰레기 주소 값이라는 의미는 메모리의 어디를 가리킬지 아무도 알 수 없다는 말이다. 그림에서는 0x24FF 였지만 내일은 무슨 주소 값이 저장될지 아무도 모른다. 그 아무도 알 수 없는 영역에 'Yeonsu Moon'이라는 문자열을 쓰려고 하기 때문에 프로그램은 더 이상 진행하지 않고 메모리 에러를 출력하는 것이다. 반대로 imsip가 안전한 영역을 가리키고 있다면 strcpy(imsip, "Yeonsu Moon")은 잘 동작할 것이다.

     적용을 시키기 이전에 초기화를 시켜주면 안정적으로 동작할 것이다.




     strcpy(dim, "Yeonsu Moon");






     앞의 그림으로 안전한 영역 20 바이트에 "Yeonsu Moon"이라는 문자열이 하나씩 복사된다는 것을 알 수 있다. 그러므로 문제를 발생시키지 않는다. 




    댓글

Designed by Tistory.