cjaizss 发表于 2009-12-29 18:35

看到C版上有人问的一个问题想到的,关于数组和指针

我用如下代码测试
/*1.c*/
#include <stdio.h>
char s[]="test\n";
void func()
{
      printf(s);
}
/2.c*/
#include <stdio.h>
#include <stdio.h>
extern char*s;
void func(void);
int main()
{
      func();
      printf(s);
      return 0;
}

编译链接再执行一下:
# gcc -c 1.c && gcc -c 2.c && gcc 1.o 2.o -o a.out
# ./a.out
test
Segmentation fault

呵呵,原因是什么呢?
如果是C版,可能这个帖子可能有点吹毛求疵,不过在这个版么,那就不算了.

jzhang918 发表于 2009-12-29 21:37

原因在于 1.c 和 2.c中 s 的类型是不一样的。在 1.c 中, s 是字符串"test\n"的起始地址。 func 调用 printf 时传递的是 s 本身的地址。 在 2.c 中,s 是一个字符指针变量。main 调用 printf 时传递的是 s 变量的值,这个值就是把 "test\n" 这个字符串以及后面的若干个字节组成的一个整数。当 printf 把这个整数当成字符串的地址去访问时,一般就会seg fault。

prolj 发表于 2009-12-29 22:02

拿这种事情吹毛求疵相当无聊,你用C思考,有人用ASM思考,我用IR思考不行么?
中国人学英语,语法特棒,交流就shit了。美国人语法特明白?那是语感,本能的反应,写代码写到背语法,简直就是杯具!

cjaizss 发表于 2009-12-30 14:16

其实我想问的问题一共有四个:
第一,为什么可以链接通过.
第二,为什么链接的时候连个warning都没有.
第三,为什么第一个test可以打出来.
第四,为什么第二个就崩了.

cjaizss 发表于 2009-12-30 14:18

再引申一个吧,你可以设想出一种编译(广义的,不分编译/汇编/链接过程)的途径让它这里产生一个错误吗?
也就是说,如果你是当初设计C语言编译器的那个人,呵呵

accessory 发表于 2009-12-31 03:37

C版的那个题目有点夸张. 如果从汇编看,我可以说"所有"的编程语言都没有数组. 因为最后都会变成汇编,而汇编里又没有数组.

关于LZ的问题,我觉得即使是设计C语言的人,他关注的也主要是语法. 在具体实现的时候难免有一些灰色地带,也就是没有明确规定的地方. 具体编译器怎么实现是不确定的.
比如MS VC 和GCC 里面某些同样的语句就可能产生不同的结果, 因为那条语句的含义本身就是模糊的,没在标准里定义的. 随便你编译器怎么弄.

kusky 发表于 2010-01-13 01:26

"退化现象 "
The implicit conversion from a function (or array) to a pointer is often called decay. ( c++ templates:the complete guide 22.2 Pointers and References to Functions)

http://www.lysator.liu.se/c/c-faq/c-2.html

lllaaa 发表于 2010-01-13 17:27

原帖由 cjaizss 于 2009-12-30 14:16 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
其实我想问的问题一共有四个:
第一,为什么可以链接通过.
第二,为什么链接的时候连个warning都没有.
第三,为什么第一个test可以打出来.
第四,为什么第二个就崩了.


1)没有未解析的符号就可以链接过,
2)a.c 的s和b.c的s对链接器来说是一样的。它不管语法上被声明成了什么类型。
3)正常现象
4)楼上有人解释了打印的是0x74736574这个地址的东西

具体原因就是
当声明数组的时候
char s[] = "test";

表示s是一个地址,那么生成汇编的代码就是
_s:
      .ascii "test\0"

当声明成字符串的时候:
char *s = "test";
表示s是一个指针,它的值是常量"test"的地址。
生成的汇编是:
LC0:
      .ascii "test\0"
      .data
      .align 4
_s:
      .long   LC0


当使用一个指针的时候,编译器会生成代码:先把s这个符号对应的地址指向的那个dword(假设是32bit系统)取出来,然后用这个值来取内容。
b.c实际就是欺骗编译器生成了错误的代码。导致b.o将test当成了地址

jzhang918 发表于 2010-01-13 20:54

原帖由 cjaizss 于 2009-12-30 14:16 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
其实我想问的问题一共有四个:
第一,为什么可以链接通过.
第二,为什么链接的时候连个warning都没有.
第三,为什么第一个test可以打出来.
第四,为什么第二个就崩了.

后面的两个问题我前面已经回答过了。后面也有人举例说明了。

前面两个问题其实是一个问题。答案:链接器不做类型检查。

jzhang918 发表于 2010-01-13 20:57

原帖由 cjaizss 于 2009-12-30 14:18 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
再引申一个吧,你可以设想出一种编译(广义的,不分编译/汇编/链接过程)的途径让它这里产生一个错误吗?
也就是说,如果你是当初设计C语言编译器的那个人,呵呵

可以把数据类型编码在符号名里,就像c++对函数名做的那样。但是这样的语言就不是现在的c语言了。
页: [1] 2 3
查看完整版本: 看到C版上有人问的一个问题想到的,关于数组和指针