当实际和外部类型不同时会发生什么?

时间:2016-03-10 03:26:09

标签: c arrays pointers extern

我有两个文件:

交流转换器

extern char *s;
int main()
{
    puts(s);
}

和b.c:

char s[] = "hello";

我同时编译它们,没有错误。但是程序在运行时崩溃了。为什么? C语言规范的哪一部分说这是非法的?

2 个答案:

答案 0 :(得分:6)

您调用未定义的行为,程序发生崩溃。

引自N1256 6.2.7兼容类型和复合类型

  

1如果类型相同,则两种类型具有兼容类型。附加规则   确定两种类型是否兼容在6.7.2中描述了类型说明符,   在6.7.3中用于类型限定符,在6.7.5中用于声明符。 [...]

     

2所有引用同一对象或函数的声明都应具有兼容的类型;   否则,行为未定义。

在典型的环境中,当程序运行时,存储的内容将被读作指针,因为声明表明指针在a.c中,但实际上是字符串的一部分(如果指针的大小)是4个字节),它几乎没有机会成为有效的指针。因此,从该地址读取很有可能导致分段错误。

答案 1 :(得分:3)

如果你想真正知道为什么 确实崩溃(而不是为什么它不应该工作):

数组是内存中的一系列事物。所以用

char s[] = "hello";

此变量的内存布局如下所示(让我们说它从0x00123400开始,带有4个字节的指针):

0x00123400:  'h'     <- address of s
0x00123401:  'e'
0x00123402:  'l'
0x00123403:  'l'
0x00123404:  'o'
0x00123405:  '\0'

要获取字符串的地址,它只使用固定数字0x00123400。

指针保存其他内容的地址。如果你有:

char *s = "hello";

然后编译器会放置数组&#34; hello&#34;某处,然后填写地址:

0x00123400:  0x00     <- address of s
0x00123401:  0x56
0x00123402:  0x78
0x00123403:  0x9A

0x0056789A: 'h'      <- what s points to
0x0056789B: 'e'
0x0056789C: 'l'
0x0056789D: 'l'
0x0056789E: 'o'
0x0056789F: '\0'

要获取字符串的地址,它从固定数字0x00123400开始,并读取该位置的数字。

现在,如果你的变量实际上是char[]并且你告诉编译器它是char*,那么它会把它当作指针。这意味着它将从变量的地址开始,读取那里的数字,并使用该数字作为字符串的地址。

那是多少?好吧,我确实说过:

0x00123400:  'h'     <- address of s
0x00123401:  'e'
0x00123402:  'l'
0x00123403:  'l'

但那是谎言 - 我们都知道记忆只存储数字,而不是字母。它只是速记,所以人们不必记住ASCII表。 真正存储的内容是:

0x00123400:  0x68     <- address of s
0x00123401:  0x65
0x00123402:  0x6C
0x00123403:  0x6C

所以你的程序会读取0x68656C6C,然后它会尝试从地址0x68656C6C开始打印字符串,这很可能是一个无效的地址。

(注意:我在这个答案中忽略了字节顺序)