#include <stdio.h>
int main()
{
char a[]="abc";
char *b="def";
a[0]='q';
b[0]='q';
return 0;
}
c언어 책에 한번쯤 나와본 예제입니다.
프로그램을 실행하면 리눅스에서 Segmentation fault 에러 납니다.
이말은 곧 메모리를 잘못 참조하였는 말인데
b[0]='q' <--- 이 부분이 잘못되었죠
보통 추상적인 메모리 구조를 보면 스택, 힙, 데이타 영역으로 크게 나누어집니다.
char *b="def"; <-- "def" 문자열은 상수입니다.
상수는 값을 바꿀수 없습니다.
c언어 타입으로 보면 const char *이죠
밑에서 b[0]='q'로 바꿀려고 하니 에러가 나는 겁니다.
이제 어셈으로 디스어셈 해보겠습니다.
int main()
{
8048394: 8d 4c 24 04 lea 0x4(%esp),%ecx
8048398: 83 e4 f0 and $0xfffffff0,%esp
804839b: ff 71 fc pushl -0x4(%ecx)
804839e: 55 push %ebp
804839f: 89 e5 mov %esp,%ebp
80483a1: 51 push %ecx
80483a2: 83 ec 10 sub $0x10,%esp
char a[]="abc";
80483a5: a1 94 84 04 08 mov 0x8048494,%eax
80483aa: 89 45 f8 mov %eax,-0x8(%ebp)
char *b="def";
80483ad: c7 45 f4 90 84 04 08 movl $0x8048490,-0xc(%ebp)
a[0]='q';
80483b4: c6 45 f8 71 movb $0x71,-0x8(%ebp)
b[0]='q';
80483b8: 8b 45 f4 mov -0xc(%ebp),%eax
80483bb: c6 00 71 movb $0x71,(%eax)
return 0;
80483be: b8 00 00 00 00 mov $0x0,%eax
}
char a[]="abc";
80483a5: a1 94 84 04 08 mov 0x8048494,%eax
80483aa: 89 45 f8 mov %eax,-0x8(%ebp)
먼저 스택에 저장되는 a값을 보겠습니다.
"abc" <--- 문자열도 상수값입니다.
0x8048494 번지에 "abc"값이 들어있습니다.
(gdb) x 0x8048494
0x80484a4: 0x00636261
0x80484a4 번지안에 --> 0x00636261 값이 있는것입니다.
61 char로 'a'입니다.
62 char로 'b'입니다.
63 char로 'c'입니다.
그런데 순서가 뒤에서 부터 시작하죠?
이건 리틀엔디안 방식으로 보이는 겁니다.
잘 모르시겠으면 네이버에 리틀엔디안 검색해 보삼
80483a5: a1 94 84 04 08 mov 0x8048494,%eax
eax레지스터에 0x8048494값이 들어가는게 아닙니다.
eax에 0x8048494안에 있는값 즉 0x00636261값이 들어갑니다.
즉 값을 복사한겁니다.
80483aa: 89 45 f8 mov %eax,-0x8(%ebp)
스택에 값을 넣고 ebp-0x8번지에 0x00636261값이 들어있습니다.
a[0]='q';
80483b4: c6 45 f8 71 movb $0x71,-0x8(%ebp)
그러면 위에 문장에서 값을 바꿔도 아무 문제 없습니다.
하지만 *b는
char *b="def";
80483ad: c7 45 f4 90 84 04 08 movl $0x8048490,-0xc(%ebp)
상수주소번지를 스택에 넣습니다.
결과를 보면
(gdb) x $ebp-0xc
0xbf819cec: 0x08048490 <--- 스택에 실주소값이 들어가 있습니다.
b[0]='q';
80483b8: 8b 45 f4 mov -0xc(%ebp),%eax
80483bb: c6 00 71 movb $0x71,(%eax)
실 주소값을 eax에 넣으면 0x8048490 값이 들어가고
eax에 간접참조하여 0x71을 넣을려고 하니 에러가 나는겁니다.
(%eax) <-- 괄호의 의미는 eax안에 값을 바꾸는게 아니라 eax안에 주소안에 있는 값을 바꾸는 겁니다.
결론은 a[]는 실제로 상수값을 복사하여 스택에 넣었고
*b는 주소값을 그대로 스택에 가져온겁니다.
80483ad: c7 45 f4 90 84 04 08 movl $0x8048490,-0xc(%ebp)
상수주소번지를 스택에 넣습니다.
결과를 보면
(gdb) x $ebp-0xc
0xbf819cec: 0x08048490 <--- 스택에 실주소값이 들어가 있습니다.
b[0]='q';
80483b8: 8b 45 f4 mov -0xc(%ebp),%eax
80483bb: c6 00 71 movb $0x71,(%eax)
실 주소값을 eax에 넣으면 0x8048490 값이 들어가고
eax에 간접참조하여 0x71을 넣을려고 하니 에러가 나는겁니다.
(%eax) <-- 괄호의 의미는 eax안에 값을 바꾸는게 아니라 eax안에 주소안에 있는 값을 바꾸는 겁니다.
결론은 a[]는 실제로 상수값을 복사하여 스택에 넣었고
*b는 주소값을 그대로 스택에 가져온겁니다.