C :: null 포인터, null 문자, 정수 0에 관한 이야기

2009/06/24 22:59

지난 어셈블리어 기말고사에서 재미있는 문제들이 나왔습니다. C 라이브러리의 strlen 함수와 strcpy 함수를 구현하라는 문제였지요. strcat 함수와 관련된 문제도 나왔구요. (이런 문제 좋아좋아^^)

문자열 함수다 보니 문자열의 종료 처리를 해야할 필요가 있었지요. 아시겠지만, C에서 문자열의 끝은 '\0' 입니다. null 문자라고 읽지요. 이걸 어셈블리어로 처리하려면 어떤 값인지를 알아야 했습니다. 여기서 시작된 선배와의 대화가 null 포인터, null 문자, 정수 0의 차이에 관한 이야기로 번졌습니다. 그 이야기를 이 글에서 다뤄보고자 합니다.

우선 아래의 코드를 보시겠어요?

#include <stdio.h>

int main (void)
{
char* sVal = NULL;
char cVal = '\0';
int iVal = 0;

printf ("%s, %c, %d\n", sVal, cVal, iVal);

return 0;
}


null 포인터(sVal)와 null 문자(cVal), 정수 0(iVal)을 출력하라는 내용입니다. null 포인터를 출력하는 부분을 %s로 지정한 이유는 이 포인터를 문자열 포인터라고 가정하였기 때문입니다. null 문자는 물론 char 형이므로 %c로 출력하라고 한 것이구요. 정수는 0은 당연히 %d 이겠죠?

사용자 삽입 이미지

위 그림에서처럼 출력 결과는 (null) 이라는 문자열로 대체된 null 포인터와 공백, 숫자 0이 나옵니다. gcc에서는 null 포인터가 저렇게 출력되는데 다른 컴파일러에서는 어떨런지 모르겠네요. 아무튼 출력 결과는 셋 다 모두 다릅니다. 그러면 실제 값들도 모두 다를까요? 다르다면 각각 어떤 값이 들어갈까요? 이것을 확인하기 위해서 코드를 어셈블리어로 뽑아 보았습니다. gcc로 컴파일할 때 -S 옵션을 주면 어셈블리어 코드를 얻을 수 있습니다.

    .file    "test.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%s, %c, %d\n"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $20, %esp
movl $0, 16(%esp)
movl $0, 12(%esp)
movl $0, 8(%esp)
movl $.LC0, 4(%esp)
movl $1, (%esp)
call __printf_chk
movl $0, %eax
addl $20, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",@progbits


코드에서 __printf_chk를 호출하는 것을 볼 수 있습니다. 이 함수는 아래와 같은 구조로 되어 있답니다.

__printf_chk (int flag, const char *format, ...)
첫 번째 flag는 출력 여부를, 두 번째 *format은 출력될 인자들의 출력형식을 나타냅니다. 출력 형식은 위에서 LC0로 제시되어 있죠. 그리고 세 번째 인자부터 출력하려는 인자들입니다. 어셈블리어에서는 C와는 인자가 들어가는 순서가 반대이므로 16~18 번째 줄의 세 개의 0이 출력하려는 인자들이 되겠습니다.

예, 보시는바와 같이 null 포인트, null 문자, 정수 0은 모두 0이라는 값을 가집니다. 기계는 이 3개의 0을 자료형에 따라 다르게 해석하는 것이지요.

결국 어셈블리어 기말고사에서는 문자열을 처리할 때 '0'을 반복 구문의 종료 조건으로 넣어야 했습니다.

덧붙임

위와 비슷한 예로 DB 프로그래밍에서 SQL 예외처리가 있습니다. null 값만 걸러서는 안 되고 공백도 같이 걸러줘야 합니다. 물론, SQL Injection에 이용되는 특수문자들도 걸러줘야 하겠죠.

덧붙임

null 문자를 뜻하는 \0은 8진수 아스키 코드입니다. 예로 하나를 들자면, \012는 LF (Line Feed)를 뜻하지요.

참고 문헌

glibc-2.3.6/debug/printf_chk.c :: https://www.codeblog.org/viewsrc/glibc-2.3.6/debug/printf_chk.c
크리에이티브 커먼즈 라이센스
Creative Commons License

6l4ck3y3 #1. 내 머리 속의 노트/C 언어 마스터 Go! gO! , , , , ,

Trackback Address:http://hisjournal.net/blog/trackback/240
  1. Blog Icon

    C 언어를 시작하지 얼마안되는 분들이...
    개념적으로 많이 혼동하는 내용이네요..^^

  2. Blog Icon
    아리새의펜촉

    전 최근 어셈블리어를 공부하면서 C와 비교하다보니 헛갈리더라구요. 그래서 정리해보았습니다.

[로그인][오픈아이디란?]