You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

头文件中使用#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 08:07:03