【问题标题】:Multiple includes of same header file in project: C vs C++项目中同一头文件的多个包含:C vs C++
【发布时间】:2020-02-24 20:11:42
【问题描述】:

这里我有一个示例项目,有两个源文件和一个头文件,如下:

main.c:

#include<stdio.h>                                                               
#include "personal.h"                                                           
int main(){                                                                     
    i = 5;                                                                        
    printf("Value is %d\n",i);                                                    
    return 0;                                                                     
 }

sub.c:

#include "personal.h"                                                           
// do nothing

最后是personal.h:

#pragma once                                                                    
int i; 

每个.c 文件都包含personal.h,它是“受保护的”。我用 gcc 编译,一切正常:

>gcc sub.c main.c -o out
>./out 
Value is 5

但是使用 g++,会发生这种情况:

>g++ sub.c main.c -o out
/tmp/cctYwVnO.o:(.bss+0x0): multiple definition of `i'
/tmp/ccPElZ27.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status

在文件链接方式、预处理器活动等方面,C++ 和 C 之间有什么根本不同吗?我对像 clang 这样的其他编译器进行了同样的尝试,并且发生了同样的情况。我可能在这里遗漏了一些愚蠢的东西。

【问题讨论】:

  • 如果我在包含的文件 'personal.h' 中声明一个 const 变量或一个类,这个错误就会消失并编译。
  • This 解释了为什么它在 C++ 中不起作用。不确定 C 是否有相同的规则。
  • 这能回答你的问题吗? What exactly is One Definition Rule in C++?
  • C 和 C++ 不同,也许这也不例外;)
  • 在头文件中初始化该变量并在C中重试链接。

标签: c++ c compilation include preprocessor


【解决方案1】:

在 C 中,

int i;

是一个暂定定义。通过包含,您可以在两个编译单元中对i 进行初步定义。 C 标准允许在多个编译单元中具有暂定定义,但不强制实现接受这一点。 Unix C 编译器的自定义行为是允许它,但 gcc 有一个选项 (-fno-common) 来阻止它并在链接时生成错误(以便您可以检查编译器的代码,就像我认为 Microsoft 的那样,它确实不允许它或允许更好代码的平台 - 我不知道,但这是 GCC 文档给出的基本原理)。

IIRC,C++ 没有这样的余量。

请注意,您可能需要声明而不是标题中的定义。因此对于上面的i 应该是

extern int i;

在标题和一个

int i;

one .c 文件中的全局范围内。

【讨论】:

  • 最后,一个实际的答案。这种行为现在很有意义。 +1
  • 如果我可以提出改进建议:添加关于为什么这是暂定定义的简短解释(对于初学者来说可能并不明显),并添加不要使用暂定定义的建议。
  • 那么声明一个可以从不同源文件访问的全局变量的标准、安全的做法是什么?假设我在 sub.c 中也有一个打印函数,它是从 main 调用的。我如何实现在 main 中初始化 i 也反映在 sub.c 中的这个打印函数中?
  • @P.Nair 在标题中再次extern int i,在一个选定的实现文件中int i
  • @P.Nair,首先,包含文件没有什么神奇之处。它们的行为就像它们的文本被包含在包含位置一样。那么你不能定义某些东西的规则是通用的,它是 C 有一个带有暂定定义的特殊情况,并且这种特殊情况并非所有编译器都允许。
【解决方案2】:

sub.c 将包含personal.h 并将在全局范围内创建变量i。同样,main.c 也将包含 personal.h 并在全局范围内创建变量 i。最终,当您链接时,全局范围内有两个 i 定义,因此会出现错误。

【讨论】:

  • 问题是,这在 C 中有何不同以及为什么不同?为什么#pragma once 不处理多个声明?
  • @P.Nair • #pragma once 在单个翻译单元的上下文中。您在两个不同的翻译单元中有#include "personal.h"
  • @P.Nair #pragma once 确保您不会在同一个翻译单元中两次包含该文件。它不会阻止同一标识符的多个定义。在头文件中,您应该使用extern int i;,并在一个翻译单元中使用int i;
  • @Bodo 见上文
  • 在 C 语言中,你可以这样做:int i; int i; int i; int i; int i;,这些多个声明是可以的,如果没有分配定义,它们将合并为一个定义。在 C++ 中,这将是 5 个冲突定义。要在 C++ 中执行相同的声明,您需要这样做:extern int i; extern int i; extern int i; extern int i; extern int i;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-04
  • 1970-01-01
  • 1970-01-01
  • 2019-04-07
  • 2018-08-03
相关资源
最近更新 更多