[Linux][C][gcc][tips] 在头文件中定义变量引发的讨论

时间:2023-01-28 14:24:49

概述

本人的原创文章,最先发表在github-Dramalife-note中。转载请注明出处。

Define variable(s) in header file referenced by multiple c files.

(CH:在 被多个c文件引用 的 头文件中定义变量)

If the variable is initialized, GCC will report an error.

(CH:如果这个变量被初始化, GCC会报错)

一般来说,如果需要,变量的声明一边放到头文件中,变量的定义不放到头文件中(尤其是被多个要被编译并链接到同一个可执行文件的源文件引用时)。

本文讨论的来源:

X:变量一定不能放到头文件中定义吗?

D:如果被多个C文件同时引用的话,gcc会报告重复定义变量的错误。

X:但是我在头文件中定义了一个变量,而且这个头文件被多个c文件引用,结果能编过!

D:??

本文源码地址1-github,

本文源码地址1-gitee镜像.

Reason_keywords : stack, heap, nm

总结

简单说明:

在 被多个源文件引用的 头文件 中定义变量,

如果这个变量没有初始化(例:int gb;),那么gcc编译时会把它放到common section,gcc在链接阶段允许有多个同名的common symbol,所以能编译成功;

如果初始化了这个变量(例:int gb = ;),gcc编译时会把变量放到data section或者bss section,gcc在链接时不允许data和bss段中的符号名重复,所以会报错-重复定义变量(multiple definition of `gb');

验证详情

0 NOTE

0.1 List files(验证时用到的文件)

验证时用到的文件(可在git获取)。

# ls
a_demo.out
config.mk
functions.c
functions.o
header.h
main.c
main.o
Makefile
README.md
source_pre.i

0.2 Manual page - nm (part)

关于nm(用于列出目标文件的符号)工具的说明。

NM(1)
"C" The symbol is common. Common symbols are uninitialized data. When linking, multiple common symbols
may appear with the same name. If the symbol is defined anywhere, the common symbols are treated as
undefined references. "B"
"b" The symbol is in the uninitialized data section (known as BSS). "D"
"d" The symbol is in the initialized data section.

0.3 Macros

代码中用于方便演示而添加的两个宏。

//header.h
/*
* __INIT_THE_VAR__ : Initialize variable"gb" in this header file.(CH:)
* Define this macro(int gb = <x>;), GCC will report error:
* (CH:开启这个宏,头文件中会定义并初始化变量gb,此时GCC会报错)
* GCC Error - multiple definition of `gb'
* (CH:重复定义变量gb)
* __VAR_VAULE_ZERO__ : Initial vaule is 0.(CH:)
* Define this macro(int gb = 0;), GCC will place the var to BSS section, else DATA section.
* (CH:开启这个宏,头文件中会定义并初始化变量gb=0,GCC会把gb变量放到BSS段,初值不为0时放到DATA段)
*/

1. Compile(编译)

1.1 DEFINE(o), INIT( ), ZERO( )

Define (but not initialize) variable in header(CFLAGS += "-U__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__")

(CH:在头文件中定义但不初始化变量)

make all-normal-create-obj CFLAGS+="-U__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__"
CLEANING...
rm -rvf ./*.out ./*.i ./*.o
removed './source_pre.i'
removed './functions.o'
removed './main.o'
rm -rvf /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/build
gcc -c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.c -o /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.o -U__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__
gcc -c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.c -o /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.o -U__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__
BUILDING...
gcc -E /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.c >> source_pre.i
FLAGS : -U__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__ ;
SRCS:
/home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.c
gcc -o a_demo.out /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.o /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.o -U__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__

1.2 DEFINE(o), INIT(o), ZERO( )

Define & initialize variable in header(CFLAGS +=" -D__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__")

(CH:在头文件中定义并初始化变量为非零值)

make all-normal-create-obj CFLAGS+="-D__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__"
CLEANING...
rm -rvf ./*.out ./*.i ./*.o
removed './a_demo.out'
removed './source_pre.i'
removed './functions.o'
removed './main.o'
rm -rvf /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/build
gcc -c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.c -o /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.o -D__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__
gcc -c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.c -o /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.o -D__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__
BUILDING...
gcc -E /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.c >> source_pre.i
FLAGS : -D__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__ ;
SRCS:
/home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.c
gcc -o a_demo.out /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.o /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.o -D__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__
/home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.o:(.data+0x0): multiple definition of `gb'
/home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
Makefile:167: recipe for target 'all-normal-create-obj' failed
make: *** [all-normal-create-obj] Error 1

1.3 DEFINE(o), INIT(o), ZERO(o)

Define & initialize variable in header(CFLAGS +=" -D__INIT_THE_VAR__ -D__VAR_VAULE_ZERO__")

(在头文件中定义并初始化变量为零)

# make all-normal-create-obj CFLAGS+="-D__INIT_THE_VAR__ -D__VAR_VAULE_ZERO__"
CLEANING...
rm -rvf ./*.out ./*.i ./*.o
removed './source_pre.i'
removed './functions.o'
removed './main.o'
rm -rvf /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/build
gcc -c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.c -o /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.o -D__INIT_THE_VAR__ -D__VAR_VAULE_ZERO__
gcc -c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.c -o /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.o -D__INIT_THE_VAR__ -D__VAR_VAULE_ZERO__
BUILDING...
gcc -E /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.c >> source_pre.i
FLAGS : -D__INIT_THE_VAR__ -D__VAR_VAULE_ZERO__ ;
SRCS:
/home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.c /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.c
gcc -o a_demo.out /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.o /home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.o -D__INIT_THE_VAR__ -D__VAR_VAULE_ZERO__
/home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/functions.o:(.bss+0x0): multiple definition of `gb'
/home/dramalife/note/70-gcc_gnu_compiler_collection/tips/Q_can_define_var_at_header/main.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status
Makefile:167: recipe for target 'all-normal-create-obj' failed
make: *** [all-normal-create-obj] Error 1

2 Symbols

2.1 DEFINE(o), INIT( ), ZERO( )

List symbol - define (but not initialize) variable in header(CFLAGS +=" -U__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__")

# nm main.o
000000000000000a r __func__.2252
U functionb
0000000000000004 C gb
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
U printf
# nm function.o
0000000000000010 r __func__.2252
0000000000000000 T functionb
0000000000000004 C gb
U _GLOBAL_OFFSET_TABLE_
U printf

2.2 DEFINE(o), INIT(o), ZERO( )

List symbol - define & initialize variable in header(CFLAGS +=" -D__INIT_THE_VAR__ -U__VAR_VAULE_ZERO__")

# nm main.o
000000000000000a r __func__.2252
U functionb
0000000000000000 D gb
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
U printf
# nm function.o
0000000000000010 r __func__.2252
0000000000000000 T functionb
0000000000000000 D gb
U _GLOBAL_OFFSET_TABLE_
U printf

2.3 DEFINE(o), INIT(o), ZERO(o)

List symbol - define & initialize variable in header(CFLAGS +=" -D__INIT_THE_VAR__ -D__VAR_VAULE_ZERO__")

# nm main.o
000000000000000a r __func__.2252
U functionb
0000000000000000 B gb
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
U printf
# nm function.o
0000000000000010 r __func__.2252
0000000000000000 T functionb
0000000000000000 B gb
U _GLOBAL_OFFSET_TABLE_
U printf

Summary(总结)

Define and/or init variable in header files referenced by multiple source files.

(CH:在 被多个源文件引用的 头文件 中 定义和初始化变量。)

变量定义时,如果不赋初值,GCC会把它放到COMMON段,COMMON类型的符号是未初始化的数据。链接时,gcc允许多个COMMON符号以相同的名称出现(在多个地方被定义)。

变量定义时,如果赋了初值,GCC会把它放到DATA或者BSS段,链接时,这两种类型的符号不允许重名。

INIT ZERO Compile Section of var
- - PASS COMMON
v - ERR DATA
v v ERR BSS