字符串存储位置的疑点

字符串存储位置的疑问

      字符串的存储位置到底在哪儿?按照我以前编汇编代码的经验,字符串肯定是放在数据段。但是在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;
}
  • VC上的输出是:
    abcd烫烫烫烫abcd
    4,16
  • 看看汇编代码吧!
    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    

       完全没有把字符串拷贝到栈空间里,栈空间里甚至连一个指针都没有。访问字符串的时候使用相对寻址,在这种情况下字符串完全存在于数据段(本例是可读写的)。总结一下,其实字符串存储的位置并不是一定要怎么样,编译器是拥有一定的*度的。我估计现在的编译器基本都会使用第一种方法,毕竟相对安全一些;其次是栈空间是动态的,函数返回后就自动释放;最后,现在内存可是白菜价呀!