我想在main.c中使用来自foo.c的变量a,我写道:
foo.c
#include <stdio.h>
int a[] = {3, 2};
void foo()
{
printf("foo\taddress of a:%x\n", a);
printf("foo\tvalue of a[0]:%x\n", a[0]);
}
main.c
#include <stdio.h>
extern int *a;
int main(void)
{
foo();
printf("main\taddress of a : %x\n", a);
printf("main\tvalue of a[0] : %x\n", a[0]);
return 0;
}
和结果输出:
foo address of a:804a014
foo value of a[0]:3
main address of a : 3
Segmentation fault (core dumped)
为什么?
答案 0 :(得分:8)
a
的类型为int[2]
,而不是int*
。再试一次
extern int a[2];
C编译器无法跨源文件进行类型检查。因此,当你说int* a
时,编译器会假设你说的是实话,使用指针语义,并且不会发出任何编译器错误。
数组和指针之间存在细微差别。我们假设一个32位系统。然后“a”的内容将像这样分发:
a 0x100 0x104 0x108 ← address +-----------+----------+ | 3 | 2 | ← content +-----------+----------+
当a
是数组时,
a
的值将转换为a
的地址。因此,当您打印a
时,您将获得其地址,即“0x100”。a[n]
相当于*(a + n)
,即将地址a
提前n
个单位,然后取消引用它以获取内容。因此,a[0]
相当于*0x100
,它将内容返回0x100,即“3”。当a
是指针时,
a
的“值”是指定地址的内容。实际上这是常态,数组类型是一种特殊情况。因此,当您打印a
时,您将获得该地址的内容,即“3”。a[n]
仍为*(a + n)
。因此,a[0]
相当于*3
,这会导致细分错误,因为地址“3”无效。答案 1 :(得分:1)
您必须对跨不同翻译单元声明的对象使用一致类型。
给定int a[] = {2, 3};
,声明extern int a[];
或extern int a[2]
;将是兼容的,而extern int *a;
不会因为指针和数组是完全独立的类型。
关于数组的一个特别之处是当数组的名称出现在任何表达式上下文中而不是作为“地址”(一元&
)或sizeof
的操作数时,它们会自动出现转换为指向其第一个元素的指针。这就是提供数组和指针之间语法兼容性的原因,但它们的类型不同。
考虑注释示例的这两个函数。请注意,表达式a
将转换为指向第一个函数的第二个(在技术上为第三个)printf
中的第一个元素的指针,但不会转换为第一个printf
中的第一个元素。操作数&
。
#include <stdio.h>
void print_array_info(void)
{
extern int a[];
printf("address of a: %p\n", (void*) &a); // prints address of a
printf(" converted a: %p\n", (void*) a); // prints address of a[0]
printf("value of a[0]: %x\n", a[0]); // prints value of a
}
void print_pointer_info(void) {
extern int a[];
int *b = a; // == &a[0]
printf("address of b: %p\n", (void*) &b); // prints address of b
printf(" value of b: %p\n", (void*) b); // prints value of b (== &a[0])
printf("value of b[0]: %x\n", b[0]); // prints value of b[0] (== a[0])
}
请注意,我使用%p
打印指针并显式转换为void*
。