看到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版,可能这个帖子可能有点吹毛求疵,不过在这个版么,那就不算了. 原因在于 1.c 和 2.c中 s 的类型是不一样的。在 1.c 中, s 是字符串"test\n"的起始地址。 func 调用 printf 时传递的是 s 本身的地址。 在 2.c 中,s 是一个字符指针变量。main 调用 printf 时传递的是 s 变量的值,这个值就是把 "test\n" 这个字符串以及后面的若干个字节组成的一个整数。当 printf 把这个整数当成字符串的地址去访问时,一般就会seg fault。 拿这种事情吹毛求疵相当无聊,你用C思考,有人用ASM思考,我用IR思考不行么?
中国人学英语,语法特棒,交流就shit了。美国人语法特明白?那是语感,本能的反应,写代码写到背语法,简直就是杯具! 其实我想问的问题一共有四个:
第一,为什么可以链接通过.
第二,为什么链接的时候连个warning都没有.
第三,为什么第一个test可以打出来.
第四,为什么第二个就崩了. 再引申一个吧,你可以设想出一种编译(广义的,不分编译/汇编/链接过程)的途径让它这里产生一个错误吗?
也就是说,如果你是当初设计C语言编译器的那个人,呵呵 C版的那个题目有点夸张. 如果从汇编看,我可以说"所有"的编程语言都没有数组. 因为最后都会变成汇编,而汇编里又没有数组.
关于LZ的问题,我觉得即使是设计C语言的人,他关注的也主要是语法. 在具体实现的时候难免有一些灰色地带,也就是没有明确规定的地方. 具体编译器怎么实现是不确定的.
比如MS VC 和GCC 里面某些同样的语句就可能产生不同的结果, 因为那条语句的含义本身就是模糊的,没在标准里定义的. 随便你编译器怎么弄. "退化现象 "
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 原帖由 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当成了地址 原帖由 cjaizss 于 2009-12-30 14:16 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
其实我想问的问题一共有四个:
第一,为什么可以链接通过.
第二,为什么链接的时候连个warning都没有.
第三,为什么第一个test可以打出来.
第四,为什么第二个就崩了.
后面的两个问题我前面已经回答过了。后面也有人举例说明了。
前面两个问题其实是一个问题。答案:链接器不做类型检查。 原帖由 cjaizss 于 2009-12-30 14:18 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
再引申一个吧,你可以设想出一种编译(广义的,不分编译/汇编/链接过程)的途径让它这里产生一个错误吗?
也就是说,如果你是当初设计C语言编译器的那个人,呵呵
可以把数据类型编码在符号名里,就像c++对函数名做的那样。但是这样的语言就不是现在的c语言了。