C语言头文件循环依赖下编译顺序与结构体定义异常咨询
头文件循环引用导致的结构体定义异常问题
问题代码
structs.h
/* * structs.h * * Created on: 14 Jun 2019 */ #ifndef STRUCTS_H_ #define STRUCTS_H_ #include <stdint.h> #include "line_mask.h" #define CONFIG_LINE_NUM 80 #endif
line_mask.h
/* * line_mask.h * * Created on: 15 Nov 2019 */ #ifndef LINE_MASK_H_ #define LINE_MASK_H_ #include "../Inc/structs.h" typedef struct { #if CONFIG_LINE_NUM <= 64 uint64_t line_mask[1]; #elif (CONFIG_LINE_NUM <= 128) uint64_t line_mask[2]; #elif (CONFIG_LINE_NUM <= 192) uint64_t line_mask[3]; #elif (CONFIG_LINE_NUM <= 256) uint64_t line_mask[4]; #else #error "Unsupported CONFIG_LINE_NUM value" #endif } LineMask; #endif
问题解答
问题1:为何编译器未报错,反而将未定义的CONFIG_LINE_NUM当作0,生成line_mask[1]的结构体?
C语言预处理阶段,未定义的宏会被直接替换为空字符串。在#if条件判断逻辑中,空值会被默认视为整数0。所以当CONFIG_LINE_NUM未定义时,#if CONFIG_LINE_NUM <= 64等价于#if 0 <= 64,这个条件成立,因此编译器会生成包含line_mask[1]的结构体。同时因为走的是第一个分支,没有触发#else里的#error语句,所以编译器不会抛出错误。
问题2:为何有头文件保护,CONFIG_LINE_NUM声明后LineMask的定义仍会变化?
头文件保护(#ifndef LINE_MASK_H_这类写法)的作用仅局限于防止同一个编译单元(单个.c文件)内重复包含同一个头文件,无法跨编译单元保证结构体定义的一致性。
不同的.c文件(编译单元)可能有不同的头文件包含顺序:
- 若编译单元A先包含
line_mask.h,此时line_mask.h会包含structs.h,但structs.h里的CONFIG_LINE_NUM是在包含line_mask.h之后才定义的,所以处理line_mask.h的条件编译时,CONFIG_LINE_NUM仍未定义,生成line_mask[1]的结构体,且头文件保护会标记LINE_MASK_H_已定义,后续在这个编译单元里再包含line_mask.h也不会重复处理。 - 若编译单元B先包含
structs.h,此时structs.h先定义了CONFIG_LINE_NUM=80,再包含line_mask.h,这时CONFIG_LINE_NUM已经是80,满足<=128的条件,生成line_mask[2]的结构体。
两个编译单元里的LineMask结构体大小不同,但C语言链接器不会检查这种类型不兼容的问题,最终程序运行时,不同模块使用的LineMask内存布局不一致,就会出现拷贝时的内存泄漏或越界问题。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

