头文件中使用#if条件编译定义Vertex结构体异常问题
条件编译Vertex结构体导致内存无效读写问题
我原本有一个可正常运行的通用图结构,Vertex类型通过void*指针实现泛型,适配BFS、DFS等算法。为简化代码,我把Vertex定义拆分到独立头文件vertex.h,用#if条件编译指令根据不同宏定义生成对应结构体类型,并引入graph.h。但实际运行时无法正确选择并分配对应Vertex类型,示例代码出现Valgrind检测到的内存无效读写错误,输出异常;仅保留单个Vertex定义时示例可正常运行。
代码文件
原Vertex定义
typedef struct vertex { void *data; char *label; bool inGraph; } vertex;
vertex.h
#ifndef VERTEX_H_INCLUDED #define VERTEX_H_INCLUDED #include <stdlib.h> #include <stdbool.h> #if defined (GRAPH_VERTEX1) typedef struct vertex { struct vertex *parent; char *label; bool inGraph; } vertex; #elif defined (GRAPH_VERTEX2) typedef struct vertex { size_t dist; char *label; bool inGraph; } vertex; #else // default typedef struct vertex { char *label; bool inGraph; } vertex; #endif #endif
main.c
#include <stdio.h> #define GRAPH_VERTEX1 #include "graph.h" int main() { vertex *v = createVertex("test"); v->inGraph = true; vertex *parent = createVertex("parent"); printf("Vertex: %s\n", v->label); printf("Parent: %s\n", parent->label); printf("In graph: %d\n", parent->inGraph); freeVertex(v); freeVertex(parent); return 0; }
graph.h
#ifndef GRAPH_H_INCLUDED #define GRAPH_H_INCLUDED #include <stdlib.h> #include "vertex.h" vertex *createVertex(char *label); void freeVertex(vertex *v); #endif
graph.c
#include "graph.h" #include <string.h> vertex *createVertex(char *label) { vertex *v = calloc(1, sizeof(vertex)); v->label = calloc(strlen(label) + 1, sizeof(char)); v->inGraph = false; strcpy(v->label, label); return v; } void freeVertex(vertex *v) { free(v->label); free(v); }
Valgrind错误信息
==986== Memcheck, a memory error detector ==986== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==986== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info ==986== Command: ./a.out ==986== ==986== Invalid write of size 1 ==986== at 0x1090FD: main ==986== Address 0x4a8e050 is 0 bytes after a block of size 16 alloc'd ==986== at 0x484DA83: calloc ==986== by 0x109279: createVertex ==986== by 0x1090F5: main ==986== ==986== Invalid read of size 1 ==986== at 0x10913A: main ==986== Address 0x4a8e0f0 is 0 bytes after a block of size 16 alloc'd ==986== at 0x484DA83: calloc ==986== by 0x109279: createVertex ==986== by 0x109108: main ==986== Vertex: (null) Parent: (null) In graph: 0 ==986== ==986== HEAP SUMMARY: ==986== in use at exit: 0 bytes in 0 blocks ==986== total heap usage: 5 allocs, 5 frees, 4,140 bytes allocated ==986== ==986== All heap blocks were freed -- no leaks are possible ==986== ==986== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) ==986== ==986== 1 errors in context 1 of 2: ==986== Invalid read of size 1 ==986== at 0x10913A: main ==986== Address 0x4a8e0f0 is 0 bytes after a block of size 16 alloc'd ==986== at 0x484DA83: calloc ==986== by 0x109279: createVertex ==986== by 0x109108: main ==986== ==986== ==986== 1 errors in context 2 of 2: ==986== Invalid write of size 1 ==986== at 0x1090FD: main ==986== Address 0x4a8e050 is 0 bytes after a block of size 16 alloc'd ==986== at 0x484DA83: calloc ==986== by 0x109279: createVertex ==986== by 0x1090F5: main ==986== ==986== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
问题原因
核心问题是编译graph.c时未定义GRAPH_VERTEX1宏:
- main.c中定义了
GRAPH_VERTEX1,使用的是包含parent指针的Vertex结构体(64位系统下大小为24字节) - graph.c编译时未传递该宏,使用默认的Vertex结构体(仅含
label和inGraph,大小16字节) - createVertex分配16字节内存,但main中按24字节的结构体访问,导致
v->inGraph的写入越界,覆盖了label指针的内存,最终label变为null,触发内存读写错误
解决方案
方案1:编译时统一传递宏参数
确保所有编译单元使用相同的宏定义,编译时通过编译器参数传递:
gcc main.c graph.c -DGRAPH_VERTEX1 -o a.out
方案2:在公共头文件中定义宏(适合固定场景)
如果不需要动态切换Vertex类型,可在graph.h开头添加宏定义:
#define GRAPH_VERTEX1
这样所有包含graph.h的文件都会使用同一版本的Vertex结构体。
方案3:添加编译时断言(提前发现问题)
在vertex.h中添加静态断言,检查结构体大小是否符合预期,避免运行时错误:
#ifndef VERTEX_H_INCLUDED #define VERTEX_H_INCLUDED #include <stdlib.h> #include <stdbool.h> #if defined (GRAPH_VERTEX1) typedef struct vertex { struct vertex *parent; char *label; bool inGraph; } vertex; #define EXPECTED_VERTEX_SIZE (sizeof(void*) + sizeof(char*) + sizeof(bool)) #elif defined (GRAPH_VERTEX2) typedef struct vertex { size_t dist; char *label; bool inGraph; } vertex; #define EXPECTED_VERTEX_SIZE (sizeof(size_t) + sizeof(char*) + sizeof(bool)) #else // default typedef struct vertex { char *label; bool inGraph; } vertex; #define EXPECTED_VERTEX_SIZE (sizeof(char*) + sizeof(bool)) #endif // 编译时断言,确保结构体大小符合预期 _Static_assert(sizeof(vertex) == EXPECTED_VERTEX_SIZE, "Vertex struct size mismatch across compilation units"); #endif
如果宏定义不一致,编译阶段会直接报错,避免运行时的内存错误。
验证结果
采用方案1编译后,Valgrind错误消失,输出正常:
Vertex: test Parent: parent In graph: 0
内容的提问来源于stack exchange,提问作者Neutrino
相关产品推荐
相关产品推荐

