字符串的存储位置到底在哪儿?按照我以前编汇编代码的经验,字符串肯定是放在数据段。但是在C/C++中,这个却不一定成立吧!昨天晚上跟群里的同学讨论,我们观点不太一样,他认为应该直接存在函数的栈空间。我可以肯定字符串常量会放在数据段,但是对字符数组我确实不太确定了。所以就分别在VC和GCC下试了试,看了个简单程序的反汇编代码。首先看看VC的吧:
#include "string.h"
#include "stdio.h"
int main()
{
char a[] = "abcd";
char b[] = {‘a‘,‘b‘,‘c‘,‘d‘};
printf("%s\n", b);
printf("%d,%d",strlen(a),strlen(b));
getchar();
return 0;
}
char a[] = "abcd";
002813C8 mov eax,dword ptr [string "abcd" (285748h)]
002813CD mov dword ptr [ebp-10h],eax
002813D0 mov cl,byte ptr ds:[28574Ch]
002813D6 mov byte ptr [ebp-0Ch],cl
char b[] = {‘a‘,‘b‘,‘c‘,‘d‘};
002813D9 mov byte ptr [ebp-1Ch],61h
002813DD mov byte ptr [ebp-1Bh],62h
002813E1 mov byte ptr [ebp-1Ah],63h
002813E5 mov byte ptr [ebp-19h],64h
后四行非常容易理解,就是立即数赋值,把字符’a’,’b’,’c’,’d’赋值到栈空间,也就是局部变量中。然后看前四行汇编,第一二行的作用是把“abcd”直接赋值给局部变量,因”abcd”只有四个字节。然后的两行是把’\0’赋值到数组a的尾部。然后再来看看strlen()的返回,strlen(a) = 4 这个结果是显而易见的,因为‘d’后有一个’\0’。然后strlen(b) = 8 ,这是因为b后边没有直接跟’\0’字符,然后strlen()函数一直寻找到字符数组a的末尾,然后最终返回。不知道为什么编译器在,数组a和b之间留了8个字节的空,所以strlen(b) = 16。
- 看看GCC的结果:
abcdabcd
4,8
汇编代码段:
080484fd <main>:
80484fd: 55 push %ebp
80484fe: 89 e5 mov %esp,%ebp
8048500: 53 push %ebx
8048501: 83 e4 f0 and $0xfffffff0,%esp
8048504: 83 ec 20 sub $0x20,%esp
8048507: 65 a1 14 00 00 00 mov %gs:0x14,%eax
804850d: 89 44 24 1c mov %eax,0x1c(%esp)
8048511: 31 c0 xor %eax,%eax
8048513: c7 44 24 17 61 62 63 movl $0x64636261,0x17(%esp)
804851a: 64
804851b: c6 44 24 1b 00 movb $0x0,0x1b(%esp)
8048520: c6 44 24 13 61 movb $0x61,0x13(%esp)
8048525: c6 44 24 14 62 movb $0x62,0x14(%esp)
804852a: c6 44 24 15 63 movb $0x63,0x15(%esp)
804852f: c6 44 24 16 64 movb $0x64,0x16(%esp)
8048534: 8d 44 24 13 lea 0x13(%esp),%eax
这是在Ubuntu 14.04中的结果(我没注意GCC版本,在Windows下码字),感觉和VC差不多。不过在早期的GCC版本中,却不是这样的。这是一个07年的老帖子中的内容:
#include "stdio.h"
int main()
{
char a[] = "abc";
a[1] = ‘a‘;
printf("%s\n", a);
}
08048394 <main>:
8048394: 55 push %ebp
8048395: 89 e5 mov %esp,%ebp
8048397: 83 ec 18 sub $0x18,%esp
804839a: 83 e4 f0 and $0xfffffff0,%esp
804839d: b8 00 00 00 00 mov $0x0,%eax
80483a2: 83 c0 0f add $0xf,%eax
80483a5: 83 c0 0f add $0xf,%eax
80483a8: c1 e8 04 shr $0x4,%eax
80483ab: c1 e0 04 shl $0x4,%eax
80483ae: 29 c4 sub %eax,%esp
80483b0: a1 bc 84 04 08 mov 0x80484bc,%eax
80483b5: 89 45 fc mov %eax,0xfffffffc(%ebp)
80483b8: c6 45 fd 61 movb $0x61,0xfffffffd(%ebp)
80483bc: 8d 45 fc lea 0xfffffffc(%ebp),%eax
80483bf: 89 44 24 04 mov %eax,0x4(%esp)
80483c3: c7 04 24 c0 84 04 08 movl $0x80484c0,(%esp)
80483ca: e8 01 ff ff ff call 80482d0 <printf@plt>
80483cf: c9 leave
80483d0: c3 ret
80483d1: 90 nop
完全没有把字符串拷贝到栈空间里,栈空间里甚至连一个指针都没有。访问字符串的时候使用相对寻址,在这种情况下字符串完全存在于数据段(本例是可读写的)。总结一下,其实字符串存储的位置并不是一定要怎么样,编译器是拥有一定的自由度的。我估计现在的编译器基本都会使用第一种方法,毕竟相对安全一些;其次是栈空间是动态的,函数返回后就自动释放;最后,现在内存可是白菜价呀!
原文地址:http://blog.csdn.net/cqu20093154/article/details/44305367