【问题标题】:if global variable have default external linkage then why can't we access it directly in another file?如果全局变量具有默认的外部链接,那么为什么我们不能直接在另一个文件中访问它呢?
【发布时间】:2016-06-28 11:39:15
【问题描述】:

我已经解决了以下问题:

上面的链接描述了如果我们在一个文件中定义全局变量并且没有指定extern关键字,由于翻译单元,它们将可以在另一个源文件中访问。

现在我有file1.c,其中定义了以下全局变量和函数:

int testVariable;

void testFunction()
{
    printf ("Value of testVariable %d \n", testVariable);
}

file2.c有以下代码

void main()
{
    testVariable = 40;
    testFunction();
}

现在我收到了error: 'testVariable' undeclared (first use in this function) -- 为什么?

注意:这两个文件都使用makefile在同一个程序中使用。

据我了解,函数和全局变量都有默认的外部链接。所以函数我们可以直接在另一个文件中使用它的名字但是变量不能为什么?

谁有想法?

编辑:

从下面的答案中,我知道就像函数旧编译器会猜测并添加隐式声明一样,但如果是变量则不能。 C99 也删除了隐式声明,但我仍然在 C99 模式下收到警告,例如:

warning: implicit declaration of function ‘testFunction’.

现在已经通过以下链接:

implicit int and implicit declaration of functions with gcc compiler

它说编译器将其作为诊断目的而不给出错误。所以编译器可以向前处理。

但是为什么在变量的情况下它不能进一步处理?即使在函数的情况下,如果编译器继续并且如果实际定义不存在,那么在链接时它也会失败。那么前进有什么好处呢??

【问题讨论】:

  • 如果编译器假定每个未声明的变量都是extern 对象,那么您永远不会收到有关缺少声明的警告。另外,编译器如何知道对象的类型?
  • @EOF 那么编译器在函数的情况下如何工作?
  • 在旧的 C 标准中,有隐式函数声明的概念。它已从较新的标准中删除,应避免使用。您可能希望使用编译器选项进行编译以使用现代 C 标准和警告。
  • @EOF 是的,我知道我们可以通过将警告视为错误来避免它。但我的问题是为什么在变量的情况下它直接视为错误?
  • 您当然可以访问它们。您只需在要访问它们的文件中声明它们。执行此操作的标准方法是将外部声明放在头文件(.h 文件)中,然后将该文件包含在您要使用它们的位置。

标签: c global-variables extern


【解决方案1】:

这里有两件事:第一是定义声明之间的区别。另一件事是translation units的概念。

定义是变量的定义,它是变量存在的实际位置,编译器为变量保留空间。

编译器需要声明才能知道符号存在于程序中某处。如果没有声明,编译器将不知道符号的存在。

翻译单元基本上非常简化了源文件及其所有包含的头文件。一个目标文件是一个单一的翻译单元,链接器会使用所有的翻译单元来创建最终的程序。

现在,一个程序只能有一个定义,例如一个全局变量可能只存在于一个翻译单元中,否则在链接时会出现多个定义错误。另一方面,声明可以存在于任意数量的翻译单元中,编译器将使用它来告诉链接器翻译引用了另一个(编译时未知)翻译单元中的定义。

所以这里发生的情况是您在file1.c 中有一个定义一个声明。该源文件用作一个翻译单元的输入,编译器为其生成一个目标文件,例如file1.o。在另一个源文件file2.c 中,没有定义,也没有声明全局变量testVariable,所以编译器不知道它的存在,并且会给你一个错误。您需要声明它,例如通过做

extern int testVariable;  // This is a declaration of the variable

函数有点复杂,因为在旧版本的 C 标准中,不必​​声明正在使用的函数,编译器会猜测并添加一个隐式声明。如果定义和隐式声明不匹配,则会导致未定义的行为,这就是在 C99 标准中删除隐式函数声明的原因。所以你也应该声明这个函数:

void testFunction(void);  // Declare a function prototype

注意这里不需要extern关键字,因为编译器可以自动判断它是一个函数原型声明。

完整的file2.c 应该是这样的

extern int testVariable;  // This is a declaration of the variable

void testFunction(void);  // Declare a function prototype

void main()
{
    testVariable = 40;
    testFunction();
}

【讨论】:

  • file1.cint testVariable的声明难道不是一个暂定定义吗?
  • void main() 应该是 int main(void)。但是,是的,原来的代码也是错误的!
  • 如果 C99 删除了函数的隐式声明,那么我仍然会收到警告 implicit declaration of function testFunction.。在 c99 模式下的 gcc 编译器上。应该不是错误????
  • @IanAbbott 不一定,这是一个神话。这并不像在每种情况下都教条地大喊“main 应该返回 int”那么简单。例如,如果 OP 的代码来自嵌入式系统,则它非常完美且符合标准。 See this for actual facts.
  • @Lundin,在独立环境中是的,但我认为默认假设应该是正在使用托管环境,void main() 是糟糕的 C 编程书籍中的内容之一一直被错误地使用。
【解决方案2】:

当编译器处理file2.c 时,它对testVariable 的存在及其类型一无所知。结果它无法生成与此类对象交互的代码。和行的目的类似

extern int testVariable;

是让编译器知道某个地方存在这样的对象并且类型为int。 对于函数,我们没有这样的问题,因为下一条规则 - 如果未定义函数 - 编译器假定它是在某个地方定义的,例如

int testFunction() { ... }

所以你可以向它传递任意数量的任意参数并尝试获取int 返回值。但是如果真正的函数签名不同 - 你会在运行时得到一个未定义的行为。由于这个弱点,这种方法被认为是不好的做法,您应该在调用该函数之前声明正确的函数原型。

【讨论】:

  • 虽然我们需要在另一个文件中编写extern关键字来访问,但是将默认外部链接添加到全局变量有什么好处?
  • IMO 没有任何好处。并且默认链接与在每个翻译单元中声明使用的顶级对象的要求无关。
  • @user2520119 没有任何好处。当没有其他说明符时,可以以一种使内部链接成为默认值的方式定义该语言,但当然它不是这样定义的。通常的做法是将外部声明放在引用它们的所有.c 文件共享的.h 文件#included 中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-08
  • 2016-10-07
  • 2014-04-12
相关资源
最近更新 更多