让我们假设我有简单的C程序,我编译它gcc -o hello hello.c:
#include<stdio.h>
main()
{
printf("hello");
}
Run Code Online (Sandbox Code Playgroud)
现在我想用strings实用程序显示"字符串" :
$ strings hello
/lib64/ld-linux-x86-64.so.2
__gmon_start__
libc.so.6
printf
__libc_start_main
GLIBC_2.2.5
fffff.
l$ L
t$(L
|$0H
hello
;*3$"
Run Code Online (Sandbox Code Playgroud)
并且,正如预期的那样,我可以在二进制文件中看到字符串"hello".
但是,当我修改我的C程序并将"hello"作为常量时:
#include<stdio.h>
char s[6] = {'h','e','l','l','o','\0' } ;
main()
{
printf("%s\n", s);
}
Run Code Online (Sandbox Code Playgroud)
我再也看不到二进制文件中的字符串"hello"了.
有人可以解释一下原因吗?
从man 1 strings(强调我的):
对于给定的每个文件,GNU字符串打印至少4个字符长的可打印字符序列(或使用下面的选项给出的数字),后跟一个不可打印的字符. 默认情况下,它只打印来自目标文件的初始化和加载部分的字符串 ; 对于其他类型的文件,它会打印整个文件中的字符串.
C语言没有将字符串定义为一等公民.它们表示为字符串数组或字符串文字.例如,在这样的基本程序中:
#include <stdio.h>
int main(void)
{
char s[] = "my string";
printf("%s\n", s);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我们可以合理地说s数组包含一个字符串.请注意,这个是在堆栈上分配的.它具有自动存储持续时间,与您的问题中的示例相反,其中s明确定义了main(和任何)功能.
现在,回到你的问题,两个程序中的两个底层对象共享相同的特征:
char[6]并具有相同的内容(C11§6.2.5/ p20),唯一的区别是,对字符串文字进行调整会调用未定义的行为,因此编译器可能会选择将它们放入单独的(例如只读)内存位置.
数组类型描述了具有特定成员对象类型的连续分配的非空对象集,称为元素类型.
具有静态存储持续时间的所有对象应在程序启动之前初始化(设置为其初始值).
从更实用的角度来看,除了该strings命令之外,您还可以使用gdb调试器分析程序,更具体地说,使用x/s命令.这是基本的例子:
$ gcc -g hello.c -o hello
$ gdb -q hello
Reading symbols from /home/grzegorz/hello...done.
(gdb) disas /m main
Dump of assembler code for function main:
6 {
0x00000000004004c4 <+0>: push %rbp
0x00000000004004c5 <+1>: mov %rsp,%rbp
7 printf("%s\n", s);
0x00000000004004c8 <+4>: mov $0x60086c,%edi
0x00000000004004cd <+9>: callq 0x4003b8 <puts@plt>
8 }
0x00000000004004d2 <+14>: leaveq
0x00000000004004d3 <+15>: retq
End of assembler dump.
(gdb) x/s 0x60086c
0x60086c <s>: "hello"
Run Code Online (Sandbox Code Playgroud)
您可能希望比较disas程序的命令结果,并查看它们之间是否存在一些差异.
| 归档时间: |
|
| 查看次数: |
2239 次 |
| 最近记录: |