当 actual 和 extern 类型不同时会发生什么?

What happens when actual and extern types are different?

我有两个文件:

a.c

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

和b.c:

char s[] = "hello";

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

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

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

1 Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in 6.7.5 for declarators. [...]

2 All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.

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

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

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

char s[] = "hello";

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

0x00123400:  'h'     <- address of s
0x00123401:  'e'
0x00123402:  'l'
0x00123403:  'l'
0x00123404:  'o'
0x00123405:  '[=11=]'

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

指针保存着其他东西的地址。如果你有:

char *s = "hello";

然后编译器会将数组"hello"放在某个地方,然后用它的地址填充s:

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: '[=13=]'

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

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

那是几号?好吧,我确实说过:

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

但那是个谎言——我们都知道记忆只存储数字,不存储字母。它只是 shorthand,所以人们不必记住 ASCII table。 真正存储的是:

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

因此您的程序将读取 0x68656C6C,然后它会尝试打印从地址 0x68656C6C 开始的字符串,这很可能是无效地址。

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