在a.c
文件中定义了数组int a[100]
,在b.c
里用extern int *a
定义,然后在两个文件里分别打印&a
和a
,前者的打印结果一致,后者打印结果不一样:
a.c: &a 0x601060, a 0x601060.
b.c: &a 0x601060, a (nil).
a.c
代码:
int a[100];
void func_a()
{
printf("%s: &a %p, a %p.\n", __FILE__, &a, a);
}
b.c
代码:
extern int *a;
extern void func_a();
int main(void)
{
func_a();
printf("%s: &a %p, a %p.\n", __FILE__, &a, a);
return 0;
}
1
glacer 2018-09-26 11:07:22 +08:00 1
你声明的是 int 指针而不是 int 数组,这不等价的。
正确声明方式是 extern int a[] |
3
Andiry 2018-09-26 11:09:35 +08:00
这是一个典型的“数组就是指针”的误解
|
4
wizardoz 2018-09-26 11:09:53 +08:00
第一个数数组,第二个是指针,怎么感觉是未定义的行为啊,编译器把指针 extern 到数组指针了。
如果我的话会在 b.c 里面 extern int a[100]; 没必要关心这些隐晦的行为。 实际上我写了几年 C,都没怎么用过 extern 变量的情况。 |
5
wizardoz 2018-09-26 11:11:34 +08:00
extern 函数对于框架代码的实现很有用。
extern 变量则很容易变成全局变量的滥用,完全可以在设计上避免 extern 变量。 |
6
pkokp8 2018-09-26 11:15:22 +08:00 via Android 1
a 文件将变量 a 当作数组解释,因此 a 地址为 a 数组首地址的地址,a 为数组 a 的首地址
b 文件将 a 当作指针解释,因此 a 地址为指针 a 的地址,同数组解释。a 为指针的值 你在 a 文件中给数组赋初值试试 别写这种代码,历史代码就改掉 |
7
emonber OP @wizardoz 感谢回复,我最近在多核实时操作系统下开发,用 extern 可以容易实现核间的变量共享。
我理解了 int a[]和 int *a 是两个类型,但是不理解为什么&a 的值是一样的。 |
8
emonber OP @pkokp8 感谢解答。所以&a 值一样是编译器实现的原因(我用 gcc 和 clang 的结果都是一样的)?
在 a.c 里定义 int a[100] = {1};后,打印结果如下: ```bash a.c: &a 0x601030, a 0x601030. b.c: &a 0x601030, a 0x1. ``` |
9
wizardoz 2018-09-26 11:20:35 +08:00 1
@emonber 我的理解是这样,extern 并不区分变量类型,而只是区分变量名。所以数组 a 和指针 a 放在了同一内存区域。
这就是&a 一样的原因 而 a 也是一样,只不过 a.c 的 a 是数组,所以打印出来就是它的实际内存地址。而 b.c 的 a 是变量,它所在的区域的值就是 null 你可以试试 a.c 中 a[0] = 1,然后在打印 b.c 中的 a,看看是否为 1. |
10
bp0 2018-09-26 11:20:55 +08:00 2
extern int *a; 在 b.c 中声明了一个弱符号。连接的时候连接器使用了 a.c 中的 a 进行了替换。
另外,全局变量未初始化时,会被放在 bss 段,也就是说被默认初始化成 0. 然后在 b.c 中使用&a,就是 a.c 中数组 a 的地址,因此相同。 但是在 b.c 中使用 a,是需要按照指针的方式进行的。也就是说用 a 中的值作为指针。因为 a.c 中 a 数组被初始化成 0。所以这个地方打印出(nil)。 总结一下,指针是要额外占用一个空间保存指向的地址的。而数组名称并不占用额外的空间,直接表示数组首地址。 |
11
innoink 2018-09-26 11:22:09 +08:00 2
extern int* a
这句话,编译器在编译 b.c 时,认为在 a.c 中的符号 a 是一个 int*变量,即符号 a 所在的内存后面 4 字节(32 位)或 8 字节(64 位)里面存放的内容是一个地址,因为全局变量给你初始化成 0 了,所以就是个 nil 但是,实际在 a.c 中的符号 a 其实是 int [100],a 是一个数组名,数组名取地址和不取地址是一样的,都是首地址,因此 a.c 中,a 和&a 是一样的 int* a 是指,有个变量 a,它指向一块连续内存,注意 a 本身也有内存 int a[100],这里的 a 可以理解为一个字面量,本身不占内存 所以 b.c 中,编译器试图在 a.c 中找 a 所占的内存,即会认为 a.c 中的符号 a 所在的内存后面 4 字节(32 位)或 8 字节(64 位)就是一个 int* 变量,而实际没有这么个变量,当然就错了 |
13
bp0 2018-09-26 11:40:54 +08:00
@innoink extern 的变量只是声明,并不是定义,所以不会被分配空间。如果最后连接时找不到 a.c 中的数组 a,那么连接会失败。
|
14
fcten 2018-09-26 11:41:20 +08:00 1
```
#include <stdio.h> int *a; int main(void) { printf("%s: &a %p, a %p.\n", __FILE__, &a, a); return 0; } ``` 400559: 48 8b 05 00 0b 20 00 mov 0x200b00(%rip),%rax # 601060 <a> ``` #include <stdio.h> int a[100]; int main(void) { printf("%s: &a %p, a %p.\n", __FILE__, &a, a); return 0; } ``` 40052a: b9 60 10 60 00 mov $0x601060,%ecx 数组是数组,指针是指针。 |
15
chiu 2018-09-26 11:45:22 +08:00 via Android 1
个人愚见:
如果 a 本身是一个数组,那么 a 等于&a,就像函数名本身是指向这个函数的指针,对函数名取址&func 也是相同的值。 如果 a 是一个指针,那么对 a 取址&a 的值就是存放这个指针变量的地址。 |
17
bp0 2018-09-26 13:08:14 +08:00 1
|
18
XiaoxiaoPu 2018-09-26 13:28:19 +08:00
@chiu a 不等于 &a,类型不一样,差距大了
|
19
chiu 2018-09-26 14:36:14 +08:00 via Android
@XiaoxiaoPu 嗯,我表述不严谨,我指值相等,既楼主所说的前者打印一样。
|