Chinaunix

标题: 看到C版上有人问的一个问题想到的,关于数组和指针 [打印本页]

作者: 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;
}

编译链接再执行一下:
[root@SRUCAU tmp]# gcc -c 1.c && gcc -c 2.c && gcc 1.o 2.o -o a.out
[root@SRUCAU tmp]# ./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 发表
其实我想问的问题一共有四个:
第一,为什么可以链接通过.
第二,为什么链接的时候连个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 发表
其实我想问的问题一共有四个:
第一,为什么可以链接通过.
第二,为什么链接的时候连个warning都没有.
第三,为什么第一个test可以打出来.
第四,为什么第二个就崩了.


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

前面两个问题其实是一个问题。答案:链接器不做类型检查。
作者: jzhang918    时间: 2010-01-13 20:57
原帖由 cjaizss 于 2009-12-30 14:18 发表
再引申一个吧,你可以设想出一种编译(广义的,不分编译/汇编/链接过程)的途径让它这里产生一个错误吗?
也就是说,如果你是当初设计C语言编译器的那个人,呵呵


可以把数据类型编码在符号名里,就像c++对函数名做的那样。但是这样的语言就不是现在的c语言了。
作者: jzhang918    时间: 2010-01-13 20:58
标题: 回复 #1 cjaizss 的帖子
另外,你是不知道这个问题的答案呢还是考考大家呢?
作者: bsdc    时间: 2010-01-23 20:26
原帖由 jzhang918 于 2010-1-13 20:58 发表
另外,你是不知道这个问题的答案呢还是考考大家呢?

还是在思考更深层的?
作者: 逸远    时间: 2010-02-06 15:16

作者: zqy1005    时间: 2010-07-30 19:00
其实我想问的问题一共有四个:
第一,为什么可以链接通过.
第二,为什么链接的时候连个warning都没有.
第三 ...
cjaizss 发表于 2009-12-30 02:16 PM



    俺只关注前两个问题,因为这两个问题的原因知道了,剩下的迎刃而解

   望版主公布答案
作者: wmmy2008    时间: 2010-07-31 17:18
1.c定义一个数组,而2.c 又声明为指针.. 那是会出大问题的。
数组和指针并非等价的。可参考<<C专家编程>>书上说的很清楚,这个问题其实很好理解....
作者: wmmy2008    时间: 2010-07-31 17:20
不好意思,没看到帖子这么老了,,,不该冒皮皮..
作者: system888net    时间: 2010-07-31 19:37
我用如下代码测试
/*1.c*/
#include
char s[]="test\n";
void func()
{
        printf(s);
}
/2. ...
cjaizss 发表于 2009-12-29 18:35



    感觉不错.
作者: system888net    时间: 2010-07-31 19:39
不好意思,没看到帖子这么老了,,,不该冒皮皮..
wmmy2008 发表于 2010-07-31 17:20



    有意义的帖不在新老。
作者: system888net    时间: 2010-07-31 19:39
原因在于 1.c 和 2.c  中 s 的类型是不一样的。在 1.c 中, s 是字符串"test\n"的起始地址。 func 调用 pri ...
jzhang918 发表于 2009-12-29 21:37



    说的好.
作者: bluesea666    时间: 2010-08-03 23:40
学习了.
作者: chinesedragon    时间: 2010-08-04 14:16
学习了!!!!




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2