隐去的字符c语言,C语言* && [] 隐式类型转换分析

昨天同事写了一个程序,代码如下:

文件A:char * p1 = "hello";

char p2[] = "world";

文件B:#include

extern char * p1;

extern char * p2;

int main()

{

puts(p1);

puts(p2);

return 0;

}

用GCC编译以后问结果如何? 回答:输出hello和world。结果hello输出了,world输出的时候发生段错误,最后将puts(p2);语句改为puts(&p2);之后正确输出,哎,颜面扫地啊,静下心来好好琢磨一番,才有了这篇文章的诞生。

分析1:

在文件A中定义的数组和指针被编译器汇编为如下代码:

.file   "a.c"

.globl p1

.section        .rodata

.LC0:

.string "hello"

.data

.align

.type   p1, @object

.size   p1, 4

p1:

.long   .LC0

.globl p2

.type   p2, @object

.size   p2, 6

p2:

.string "world"

.ident  "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-46)"

.section        .note.GNU-stack,"",@progbits

注意以p1和p2定义的差别,p1实际是一个long大小的空间,里面存储的是.LC0标签的位置,而.LC0标签的位置代表了字符串的位置,所以在文件b中使用以下语句: puts(p1);p1代表的是一个指针,通过指针里面的值可以找到字符串的位置,所以正确输出。

在使用puts(p2)语句的时候,其实p2在内存中的位置里面直接放的是字符串,从char p2[] = "world";到extern char * p2;其实发生了一个转变,编辑将p2当成纯粹的一个指针来使用,即在文件B中p2和p1在编译器的眼中是一样的,但其实定义的时候并不一样,这样从p2的位置取字符串的地址然后通过这个地址取的字符串显然会崩溃,因为p2中本来放的就是字符串,将字符串当成地址再取字符串,鬼知道会指到哪里去,所以崩溃。

但是使用puts(&p2);语句的时候能正确,是因为编译器将p2标签的地址传递puts函数,这样从p2的地址取的p2标签的位置,然后再p2标签的地方获取字符串就正确了,文件B生成的汇编代码可以看出差距:

// puts(p1)语句生成的汇编代码

movl   p1, %eaxmovl    %eax, (%esp)

call    puts

// puts(p2)语句生成的汇编代码

movl   $p2, %eaxmovl    %eax, (%esp)

call    puts

分析2

所以,通过分析1可以看出来这样的extern使用会出问题,但在函数中使用则没有问题,如下所示:

void foo(char [] p) { puts(p); }

void foo2(char * p) { puts(p); }

对foo和foo2函数传递指针和数据名没问题,因为编译器自己在里面会处理,有兴趣的可以看看生成的汇编代码。

总结: 在一个文件中定义字符数组,如果在别的文件中extern成指针的时候,感觉其实隐含了一个类型转换,但这个转换虽然没有警告,但不代表是正确的,程序运行的时候会崩溃!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值