[C/C++] printf("%s\n", NULL)/ (null) 출력 / segmentation fault

strtok 사용하기

오늘도 열심히 C언어 공부를 하던 중 흥미로운 사실을 발견해서 기록용으로 남겨놓는다. 문자열을 자르는 함수 strtok을 이용하여 ' ' 공백 문자 기준으로 잘라낸 다음 문자열 포인터에 잘라낸 문자열 주소를 저장한다. 그 다음 잘라낸 문자열들을 출력하는 예제이다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char *s1 = malloc(sizeof(char) * 30);
    char *s2 = "The Little Prince";
    strcpy(s1, s2);

    char *sArr[10] = {
        NULL,
    }; // NULL로 배열 초기화


    // 공백 문자 기준으로 잘라 sArr에 저장
    char *ptr = strtok(s1, " ");
    int ii = 0;
    while (ptr != NULL)
    {
        sArr[ii] = ptr;
        ii++;

        ptr = strtok(NULL, " ");
    }


    // 저장된 문자열 출력
    for (int jj = 0; jj < 10; jj++)
    {
        printf("%s\n", sArr[jj]);
    }

    free(s1);
    return 0;
}

출력 결과

 The
 Little
 Prince

정답과 다른데..😒

sArr배열은 4번째 요소부터 10번째까지는 NULL을 값으로 가지고 있지만 아무 문제 없이 출력되는 것을 확인할 수 있다. 그런데 원래 정답은 문자열 출력 부분이 다음과 같다.

    ...
    
    for (int i = 0; i < 10; i++)
    {
        if (sArr[i] != NULL)           // 문자열 포인터 배열의 요소가 NULL이 아닐 때만
            printf("%s\n", sArr[i]);   // 문자열 포인터 배열에 인덱스로 접근하여 각 문자열 출력
    }
    
    ...

왜 여기서 sArr의 요소가 NULL이 아닌 경우에만 출력하는 것인지 궁금해서 여러 가지를 테스트해보았다. 예를 들면

printf(" %s\n", sArr[jj]);
printf("test%s\n", sArr[jj]);

와 같이 변경하여 컴파일하면 다음과 같이 출력된다.

 The
 Little
 Prince
 (null)
 (null)
 (null)
 (null)
 (null)
 (null)
 (null)

NULL은 항상 주의

이것은 원래 printf가 표준 입력, 즉 ANSI 코드만 받아들이기로 설게되어 있기 떄문에 그 외에 NULL이 입력되는 경우는 Undefined behavior라 컴파일러마다 다른 행동을 보이게 된다.

PS C:\Users\user\Desktop\코딩공부해요\CC++> gcc --version
gcc.exe (MinGW.org GCC-6.3.0-1) 6.3.0

나는 gcc를 사용하고 있기 때문에 위와 같이 행동하지만, 다른 컴파일러에서는 segmentation fault를 일으킨다. gccNULL이 입력될 경우 puts()함수를 호출하여 문제가 없이 실행된다.

참고 : https://stackoverflow.com/questions/11589342/what-is-the-behavior-of-printing-null-with-printfs-s-specifier