您将获得以下两个C文件:
#include <stdint.h>
#include <stdio.h>
extern uint32_t *foo;
int main() {
printf("%p\n", foo);
printf("%x\n", *foo);
}
和
#include <stdint.h>
uint32_t foo[2] = {0xDEADBEEF, 0xCAFEFEED};
假设您正在x86_64处理器上运行,那么当您将这两个文件编译并链接在一起时会发生什么?更重要的是,为什么?
答案 0 :(得分:3)
益智游戏有单独的“堆叠”网站:https://codegolf.stackexchange.com/
在你的情况下,你是骗你的编译器。您可以将'foo'定义为第二个文件中数组的名称,并将其定义为“main”文件中的指针。数组和指针是不同的概念。
如果您将main中的extern声明更改为与第二个模块中的相同,您将可以: extern uint32_t foo [];
补充:如果你“内联”foo并替换 extern uint32_t * foo; 同 uint32_t foo [2] = {0xDEADBEEF,0xCAFEFEED}; 然后编译器将看到您的变量不是指针而是数组的名称。就像你做extern unit32_t foo []时一样。例如,请检查:Is an array name a pointer?。
答案 1 :(得分:2)
你会得到
DEADBEEFCAFEFEED
Segmentation fault
因为C数组是直接存储的,所以没有中间引用或指向它们的指针。我想你会发生这种情况:
第2步很容易让人期待,因为在很多情况下,数组就像指针一样,即:
int a[2] = {1, 3};
...
*a
但它们是不是指针,它只是C编译器通过说*a
知道你的意思。您可以参考:
int a[2] = {1, 2};
printf("%p %p\n", a, &a); /* Prints same values */
所以这里真的发生了什么:
答案 2 :(得分:1)
正如other answer指出的那样,你向编译器谎称foo
的类型。因此,您的程序具有未定义的行为,在这种情况下会导致分段错误。
将变量声明为extern
时,您应该永远直接将extern
语句放入.c
文件中。您应该始终将extern
语句放入头文件中,然后#include
在需要它的任何.c
文件中添加该标头。但最重要的是,您应该始终在定义变量的.c
文件中包含该标头,以便编译器可以针对变量定义验证extern
声明。
所以代码应该由下面显示的三个文件组成
#include <stdint.h>
extern uint32_t *foo;
#include <stdint.h>
#include "foo.h"
uint32_t foo[2] = {0xDEADBEEF, 0xCAFEFEED};
#include <stdio.h>
#include <stdint.h>
#include "foo.h"
int main( void )
{
printf("%p\n", (void *)foo);
printf("%x\n", *foo);
}
在这种情况下,您从gcc
收到的错误消息是
foo.c:3:错误:'foo'的冲突类型
foo.h:2:错误:先前'foo'的声明在这里
当然,您需要通过修复foo.h
#include <stdint.h>
extern uint32_t foo[];