为什么我的 #ifndef 预处理器指令在编译时不能阻止 header 的多次包含?

Why doesn't my #ifndef preprocessor instruction prevent multiple inclusions of a header when compiling?

编辑:解决了。感谢@Govind-Pramar 和@SomeWittyUsername 提供解决方案,在 header 中将常量声明为 extern 并在 C 文件中初始化它们。

我正在做这个项目:https://github.com/SuperTotoGo/AES_Cipher

并且我有一个header文件,其中包含需要被项目的其他模块访问的常量(所有错误都与"aes_const.h"中定义的常量有关),所以是包含在所有所述模块中。 为了避免多重包含,我使用了#ifndef/#define 预处理器指令,但是在编译时出现了这个错误:

gcc -std=c99 -o aes.out aes_ciph_func.c aes_kexp_func.c aes_math.c main.c
usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x0): multiple definition of `AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x0): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x100): multiple definition of `INV_AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x100): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x200): multiple definition of `AES_LOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x200): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x300): multiple definition of `AES_ALOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x300): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x400): multiple definition of `AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x400): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x410): multiple definition of `INV_AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x410): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x420): multiple definition of `RCON'; /tmp/ccC4gp1r.o:(.rodata+0x420): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x0): multiple definition of `AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x0): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x100): multiple definition of `INV_AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x100): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x200): multiple definition of `AES_LOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x200): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x300): multiple definition of `AES_ALOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x300): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x400): multiple definition of `AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x400): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x410): multiple definition of `INV_AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x410): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x420): multiple definition of `RCON'; /tmp/ccC4gp1r.o:(.rodata+0x420): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x0): multiple definition of `AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x0): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x100): multiple definition of `INV_AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x100): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x200): multiple definition of `AES_LOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x200): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x300): multiple definition of `AES_ALOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x300): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x400): multiple definition of `AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x400): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x410): multiple definition of `INV_AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x410): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x420): multiple definition of `RCON'; /tmp/ccC4gp1r.o:(.rodata+0x420): first defined here
collect2: error: ld returned 1 exit status

如果我的理解没有错的话,是我的header被多次收录造成的。

aes_const.h 看起来像这样:

#include <stdint.h>

#ifndef AES_CONST_H
#define AES_CONST_H

//CONSTANTS ARE DECLARED HERE

#endif

我的模块基本上是这样的:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include "aes_kexp_func.h"
#include "aes_const.h"

//FUNCTIONS ARE IMPLEMENTED HERE

包含模块函数原型的header:

#ifndef MODULE_NAME_H
#define MODULE_NAME_H

//FUNCTIONS PROTOTYPES ARE HERE

#endif

和我的主文件:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include "aes_const.h"
#include "aes_ctypes.h"

#include "aes_math.h"
#include "aes_ciph_func.h"
#include "aes_kexp_func.h"

main(){/*code here*/}

我的#ifndef 不应该阻止这种情况发生吗?我错过了什么?

#ifndef 是编译时指令。您在链接期间遇到问题。您的每个源文件都是单独编译的,但之后它们会链接在一起。因此,如果您在多个源文件包含的头文件中定义了常量,您将在链接期间发生冲突。头文件的经验法则 - 声明那里的数据但不要定义它。如果你有常量需要被多个模块访问,在一个模块中定义它们并在共享头文件中将它们声明为extern

Include guards(#ifndef _HEADER_NAME_#pragma once 形式)防止相同的 header 在同一个翻译单元中重复包含,而不是在不同的 TU 中重复包含。

您可以做的是:

  1. extern声明替换aes_const.h中的常量定义,像这样:

    extern const uint8_t AES_SUB_BOX[16][16];
    
  2. 在您的任何源文件中,完整定义全局变量:

    const uint8_t AES_SUB_BOX[16][16] = { /* your initialization here */ };
    

你犯了一个简单的错误。您在 .h 文件中声明真实数据和可能的代码。您应该只在 C 文件中执行此操作。在 .h 文件中,所有变量都应声明为 extern,它只发出符号而不是对象本身。