这里我有两个文件externdemo1.c
和externdemo2.c
.在第一个文件中,我已经声明并初始化了一个字符数组arr
在文件作用域。但是我在第二个文件externdemo2.c
中声明了它,没有extern
关键字,并在函数display()
中使用了它。以下是我由此产生的困惑。请回答这三个问题:
//File No.1--externdemo1.c
#include<stdio.h>
#include "externdemo2.c"
extern int display();
char arr[3]={'3','4','7'};
//extern char arr[3]={'3','4','7'};
//extern int main()
int main()
{
printf("%d",display());
}
//File No.2--externdemo2.c
char arr[3];
int display()
{
return sizeof(arr);
}
1)为什么程序编译良好,即使我已经声明了arr
没有extern
关键字在externdemo2.c
?我读到函数的默认链接是外部的,但我不确定如果是这样,即使是变量。我只知道全局变量有extern
存储类。
2) extern storage class
和extern linkage
的严格区别是什么?我迫切需要澄清这一点。在第一个文件中,我定义了数组arr
,我没有使用关键字extern
,但我知道它默认具有extern storage
类。但是在第二个文件中,是否有任何默认的extern
,存储类或链接,关于全局变量arr
,即在externdemo2.c
中?
3)检查第一个文件externdemo1.c
中的注释行。只是为了测试它,我使用了extern char arr[3]={'3','4','7'};
行,但是它给出了错误'arr' initialized and declared 'extern'
。这个错误意味着什么?我还提到了一个注释行extern int main()
,但它工作得很好,没有错误或警告。那么,为什么我们可以在函数中使用extern
,即使函数默认是extern
,但不能用于变量,比如这里的arr
?
请花点时间帮我解决这件事。它将消除我对整个extern
事件的大部分挥之不去的疑虑。这将是巨大的帮助,如果你能回答所有3位1),2)和3)。Especially 3) is eating my brains out
主要问题
-
基本上,因为您已经在
externdemo1.c
文件中包含了externdemo2.c
的源代码。 -
这是一个大问题。因为没有初始化式,所以
externdemo2.c
中的char arr[3];
行生成数组arr
的暂定定义。当遇到具有初始化的实际定义时,暂定定义不再是暂定定义,但也不是重复定义。关于
extern storage class
vsextern linkage
…链接是指是否可以从定义符号的源文件外部看到符号。具有extern linkage
的符号可以被其他正确声明该符号的源文件通过名称访问。在定义的范围内,extern storage class
表示"存储在函数作用域之外",因此独立于任何函数。使用exn存储类定义的变量可能有外部链接,也可能没有。因为没有使用关键字
static
定义,所以数组arr
具有外部链接; -
注释掉的行没有注释掉,你有一个数组的两个定义,这是不允许的。
我观察到你必须只编译externdemo1.c
来创建一个程序——编译器包含了externdemo2.c
的代码,因为它是直接包含的。您可以从externdemo2.c
创建一个对象文件。但是,您不能通过链接externdemo1.c
和externdemo2.c
的目标文件来创建程序,因为这会导致函数display()
的多个定义。
我已经把两个文件放在[同一个目录]了。如果我没有在第一个文件中包含第二个文件,那么当我编译第一个文件时,它会显示错误
undefined reference
。因为我在第一个文件中使用了extern
函数,即使我不包括第二个文件,链接器也不应该链接到它吗?或者链接器只在默认文件夹中查找?
这里有几个混淆。让我们试着一个一个地解决它们。
与
链接器(通常由编译器启动)将链接命令行中指定的目标文件和库。如果您想要两个目标文件,将它们称为externdemo1.obj
和externdemo2.obj
,链接在一起,您必须告诉链接器(通过IDE中的构建系统)它需要处理这两个目标文件-以及默认情况下不拾取的任何库。(标准C库,加上特定于平台的扩展,通常会自动拾取,除非您特意阻止这种情况发生。)
链接器没有义务花任何时间寻找可能满足引用的零散目标文件;实际上,期望它只链接那些被告知要链接的目标文件和库,而不是随意添加其他文件和库。这里有一些关于库的警告(如果一个库被告知要链接,它可能会添加一些没有在命令行中提到的库),但是链接器不会添加额外的对象文件。
c++与模板实例化的可能被认为有点不同,但实际上它遵循了大致相同的规则。
源代码你应该有一个标题,externdemo.h
,它包含:
#ifndef EXTERNDEMO_H_INCLUDED
#define EXTERNDEMO_H_INCLUDED
extern int display(void);
extern char arr[3]; // Or extern char arr[]; -- but NOT extern char *arr;
#endif /* EXTERNDEMO_H_INCLUDED */
您应该修改源文件以包含头文件:
//File No.1--externdemo1.c
#include <stdio.h>
#include "externdemo.h"
char arr[3] = { '3', '4', '7' };
int main(void)
{
printf("%dn", display());
return 0;
}
:
//File No.2--externdemo2.c
#include "externdemo.h"
int display(void)
{
return sizeof(arr);
}
唯一棘手的问题是"externdemo2.c
真的知道arr
的大小吗?"答案是"是的"(至少在Mac OS X 10.8.3上使用GCC 4.7.1)。但是,如果头文件中的extern声明没有包含size (extern char arr[];
),则会得到如下编译错误:
externdemo2.c: In function ‘display’:
externdemo2.c:7:18: error: invalid application of ‘sizeof’ to incomplete type ‘char[]’
externdemo2.c:8:1: warning: control reaches end of non-void function [-Wreturn-type]
您的程序看起来有点错误。对我来说,#include "externdemo2.c"行无效。
以下是我所做的更正,它有效。
//File No.1--externdemo1.c
#include <stdio.h>
extern char arr[3];
extern int display();
int main()
{
printf("%d", arr[0]);
printf("%d",display());
}
//File No.2--externdemo2.c
char arr[3]={'3','4','7'};
int display()
{
return sizeof(arr);
}
请点击以下链接以便更好地理解:
- extern关键字对C函数的影响
- 如何使用extern在源文件之间共享变量?
使用#include
将使这两个文件仅作为一个文件。可以使用标志-E
检查中间文件,如下所示:
gcc -E externdemo1.c