如何在 C 中声明结构以避免结构损坏?

How to declare structures in C to avoid structure corruption?

我遇到了与 this question and my problem was solved similar to this answer 类似的问题,但我不明白是什么导致了这个问题。 我在 app.h.

中声明了三个结构 FileLineBuffer
typedef struct File {
    FILE *fs;
    char *path;
    size_t size;
} File;

typedef struct Line Line;
struct Line {
    char *text;
    size_t len;
    size_t line_no;
    Line *next;
    Line *prev;
};

typedef struct Buffer {
    int id;
    File file;
    Line *first;
    Line *last;
    Line *current;
    int x_pos;
    int y_pos;
    int visual_x;
    bool modified;
} Buffer;

Makefile如下:

CC = gcc
CFLAGS = -Wall -Werror -g
LDFLAGS = 
LDLIBS = -lcurses
OBJECTS = app.o io.o global.o move.o winio.o utils.o

all: app

app: $(OBJECTS)
    $(CC) $(LDFLAGS) $(OBJECTS) $(LDLIBS) -o $@

%.o: %.c proto.h app.h
    $(CC) $(CFLAGS) -c $<

.PHONY: clean
clean:
    -rm app
    -rm *.o

Buffer结构有一个全局指针:extern Buffer *bufferproto.h中声明,在global.c中定义,*buffer在[=中动态分配23=]。 io.c 中有几个函数与 *buffer 一起使用。我将 io.cshow_buffer() 功能移动到另一个模块,例如 winio.c

void show_buffer()
{
    size_t i = 0, j = 0;
    Line *it;

    for (it = buffer->first; it != NULL && i < (LINES - STATBAR_HEIGHT);
            it = it->next, i++) {
        for (j = 0; it->text[j] != '[=12=]'; j++) {
            waddch(mainwin, it->text[j]);
        }

    }
}

我还删除了 File 结构的 size 成员。然后 show_buffer() in winio.c 不再起作用了。我发现 show_buffer() 正在使用损坏的 buffer->first 指针。我重新编译了每个模块,但没有用。

当我再次添加 File 结构的 size 成员时问题解决了。 我也发现如果我将 show_buffer() 移回到io.c,问题就解决了。我怀疑结构填充可能会导致问题。

我的问题是导致问题的原因以及如何避免。

分辨率

预编译的 header 导致了问题,因为它没有被重新编译。

结构定义是 compile-time 数据。编译源文件后,生成的二进制组件将采用的 struct 布局是固定的。如果您无法确保使用给定 struct 类型的系统的所有组件对该类型的布局具有相同的想法(如下所述),那么它很容易导致数据损坏。您很幸运,在您的案例中,损坏很容易识别。

您的 struct 定义应该出现在 header 文件中(仅),并且每个依赖它的源文件都应该 #include header。如果您通过添加或删除成员、更改成员类型或重新排序成员来修改 struct 的结构,则必须重新编译依赖于 header 的所有源文件。 (任何 halfway-decent 构建系统都可以帮助您轻松完成。)

通过使用指向 struct 类型的指针而不是 struct 本身,您可以在某种程度上使自己免受此类问题的影响,但这只有在您需要取消引用指针时才有用。

我回答我的问题,希望它对其他人有用。在调查问题原因时,我偶然发现工作目录中有一个 预编译头文件 文件 app.h.gch。我怀疑在我的 Makefile 的以前版本中,我已经将 app.h 传递给了编译器而不自知。不幸的是,Makefile 中没有删除预编译头文件的规则。当我删除 app.h.gch 并重新编译整个项目时,一切恢复正常。