C语言数组与指针定义分析

      昨天同事写了一个程序,代码如下:
      文件A:
          char * p1 = "hello";
          char p2[] = "world";

      文件B:
          #include <stdio.h>

          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 4
              .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, %eax
      movl    %eax, (%esp)
      call      puts

 

      // puts(p2)语句生成的汇编代码
      movl    $p2, %eax
      movl    %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、付费专栏及课程。

余额充值