2013-12-17 29 views
1

我在看char* c = "thomas";char c[] = "thomas";之间的C差异。我在这里看到了一些关于这个问题的问题,同时试图理解我想通过查看程序集来检查我是否正确的答案。并且有几个问题诞生了。解释编译代码结构和静态分配

下面是我想:

  1. char* c = ...:字符的静态存储器分配的地方(只从程序的角度看),旁边的代码。这就是为什么它应该被标记为const。字符串可以打印但不能修改。

  2. char c[] = ...:同1不同之处在于当一个函数被调用时,字符被复制在堆栈上的阵列,所以可以等等被修饰

我想检查此所以我提出此的C代码:

#include <stdio.h> 

int  main(){ 
    char c [] = "thomas blabljbflkjbsdflkjbds"; 
    printf("%s\n", c); 
} 

看着所产生的组件:

0x400564 <main>: push rbp 
    0x400565 <main+1>: mov rbp,rsp 
    0x400568 <main+4>: sub rsp,0x30 
    0x40056c <main+8>: mov rax,QWORD PTR fs:0x28 
    0x400575 <main+17>: mov QWORD PTR [rbp-0x8],rax 
    0x400579 <main+21>: xor eax,eax 
    0x40057b <main+23>: mov DWORD PTR [rbp-0x30],0x6978616d 
    0x400582 <main+30>: mov DWORD PTR [rbp-0x2c],0x6220656d 
    0x400589 <main+37>: mov DWORD PTR [rbp-0x28],0x6c62616c 
    0x400590 <main+44>: mov DWORD PTR [rbp-0x24],0x6c66626a 
    0x400597 <main+51>: mov DWORD PTR [rbp-0x20],0x73626a6b 
    0x40059e <main+58>: mov DWORD PTR [rbp-0x1c],0x6b6c6664 
    0x4005a5 <main+65>: mov DWORD PTR [rbp-0x18],0x7364626a 
    0x4005ac <main+72>: mov BYTE PTR [rbp-0x14],0x0 
    0x4005b0 <main+76>: lea rax,[rbp-0x30] 
    0x4005b4 <main+80>: mov rdi,rax 
    0x4005b7 <main+83>: call 0x400450 <[email protected]> 
    0x4005bc <main+88>: mov rdx,QWORD PTR [rbp-0x8] 
    0x4005c0 <main+92>: xor rdx,QWORD PTR fs:0x28 
    0x4005c9 <main+101>: je  0x4005d0 <main+108> 

所以字符被复制到堆栈中,这就是我的想法。

问题:

  1. 的字符的字节位于地址0x6978616d, 0x6220656d存储等。为什么不在数组中连续分配?编译器的简单优化?

    1. 解释了为什么char*没有表现得像一个数组,为什么c[10]不是字符串的字符11。 然而它没有解释为什么

    的char * C = “托马斯blabljbflkjbsdflkjbds”; printf(“%s \ n”,c);

工程。 (请注意[] - > *)。我猜printf字符按字符读取,直到达到0,所以只知道c (i.e &c[0])如何访问c[10]? (因为不连续,并且事实上这次的字符没有被拷贝到栈上的数组)。

我希望我很清楚,如果你问/不明白某个观点,我可以重新阐述。谢谢

回答

3

1:0x6978616d,0x6220656d不是地址,它是你的字符串中的数据。当从十六进制转换为ASCII时,0x6978616d = moht, = b sa

2:当在函数调用中使用时,数组衰减为指针。因此printf将收到一个指向char的指针,无论c是数组还是指针。

+0

我将增加对2串实际上表示为静态数组附近的代码,我看到它时,我看拆装'焦某处* c =“lksdlkfjsd”; printf(“%s \ n”,c);'。所以它不是好像阵列是分散的,第二个问题不需要。 – Thomas

1

编译器实际上可能会选择将字符数组初始化作为只读存储器的副本进行编译,但正如Klas所示,在您的示例中没有发生这种情况。

下面是一个代码的例子(使用gcc)。将STR的定义更改为各种长度的字符串并查看程序集输出的差异可能很明显。

/* 99 characters */ 
#define STR "123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789" 

void observe(const char *); 

void test1() { 
    char *str = STR; 
    observe(str); 
} 
void test2() { 
    char str[] = STR; 
    observe(str); 
} 

和大会:

.section .rodata.str1.4,"aMS",@progbits,1 
    .align 4 
.LC0: 
    .string "123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789" 

    .text 
test2: 
    pushl %ebp 
    movl $25, %ecx 
    movl %esp, %ebp 
    subl $136, %esp 
    movl %esi, -8(%ebp) 
    movl $.LC0, %esi 
    movl %edi, -4(%ebp) 
    leal -108(%ebp), %edi 
    rep movsl 
    leal -108(%ebp), %eax 
    movl %eax, (%esp) 
    call observe 
    movl -8(%ebp), %esi 
    movl -4(%ebp), %edi 
    movl %ebp, %esp 
    popl %ebp 
    ret 

test1: 
    pushl %ebp 
    movl %esp, %ebp 
    subl $24, %esp 
    movl $.LC0, (%esp) 
    call observe 
    leave 
    ret 
+0

我没有看过程序集,那你能用英文解释你看到的区别吗?谢谢 – Thomas

+0

有趣的位是'movl $ .LC0,%esi',它加载了只读存储器的地址,'rep movsl'是块复制指令。将它与文章中的程序集进行比较:根本没有副本,只是将即时值存储到堆栈中。 – gsg

+0

非常有趣,我开始看到你的意思(除了块复制:复制块到哪里?)。为什么你认为编译器在test2堆栈上保留0x108字节左右,如果它不在那里复制字符? – Thomas