这两种代码有什么区别?

时间:2010-11-12 01:47:55

标签: c compiler-construction

以下是两种具有相同输出的代码片段。

char *p = "abc";
::printf("%s",p);

::printf("%s","abc");

“abc”字符串存储在内存中的位置有什么不同吗?

我曾经听说在第二个代码中,“abc”字符串由编译器放在只读内存中(.text部分?)

如何判断代码中的这种差异?

非常感谢。

更新

我目前的理解是:

当我们写:

char *p="abc"

虽然这似乎只是 声明性声明 ,但实际上编译器会生成许多 命令性说明 为了它。这些指令将在包含方法的堆栈框架内分配适当的空间,它可以是这样的:

subl %esp, $4

然后将“abc”字符串的地址移动到该分配的空间,它可能是这样的:

movl $abc_string_address, -4(%ebp)

“abc”字符串存储在可执行文件映像中。但是在内存中它(我的意思是字符串)将被加载完全取决于编译器/链接器的实现,如果它被加载到进程的地址空间的只读部分(即存储器页面的保护位标记为只读,然后p是只读指针,如果它被加载到r / w部分,则p是可写的。

如果我错了,请纠正我。现在我正在查看gcc生成的汇编代码,以确认我的理解。我很快就会再次更新这个帖子。

4 个答案:

答案 0 :(得分:3)

存储字符串文字的位置没有区别。唯一的区别是前者还在堆栈上为变量分配空间来存储指针。

答案 1 :(得分:3)

  

“abc”字符串存储在内存中的位置有什么不同吗?

不,两者都是如此。字符串文字存储在只读段中。但是,如果将变量声明为char [],它将被复制到堆栈中,即不是只读的。

答案 2 :(得分:1)

除了第一个在堆栈上分配的char指针之外没有区别。

它们都使用字符串文字,用双引号分隔。

答案 3 :(得分:1)

是的,它不会存储在不同的位置,它们都是编译时知道的变量,因此编译器会生成汇编代码,而“abc”字符串将在数据段上,即初始化数据。 .bss部分用于单位化数据。

尝试使用gcc和-s选项进行编译。它将生成一个.s文件,它是汇编代码。 “abc”变量将位于.rodata段下,与NASM程序集的.data相同。

如果你不想做这项工作,这是汇编代码:

这是char * c =“abc”; printf(“%s \ n”,c);  注意这个文件的代码行多于另一个,因为这段代码分配了一个指针变量,并打印了这个变量,另一个解决方案不使用变量,它只是引用一个静态的内存地址。

    .file   "test.c"
    .section    .rodata
.LC0:
    .string "abc"
    .text
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    $.LC0, 28(%esp)
    movl    28(%esp), %eax
    movl    %eax, (%esp)
    call    puts
    movl    $0, %eax
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

这适用于printf(“abc \ n”);

    .file   "test2.c"
    .section    .rodata
.LC0:
    .string "abc"
    .text
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $16, %esp
    movl    $.LC0, (%esp)
    call    puts
    movl    $0, %eax
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

要编辑:

我认为任何编译器都不会将p放在只读内存上,因为你在代码中声明它是一个变量,它不是编译器生成的隐藏/保护变量,它是一个变量你可以随时使用。

如果执行char* p = "abc";,编译器会将指针的大小分配给堆栈,并且在后面的指令中,它将插入“abc”字符串的内存地址(现在将其放入只读)寄存器,如果编译器需要注册,则将其值保存到堆栈中。

如果执行printf("abc");,则不会对变量进行分配,因为编译器在编译时知道字符串的值,所以它只是在那里插入一个数字(相对于可执行文件的开头)并且它可以读取内存的那部分内容。  在此选项中,您可以编译它,生成.exe,然后使用HEX编辑器,并搜索“abc”字符串,并将其更改为“cba”或其他任何内容(可能它将是第一行或其中一行)最后一个),如果编译器像这样生成一个简单的.exe,这很可能。