如何在 C 中声明结构以避免结构损坏?
How to declare structures in C to avoid structure corruption?
我遇到了与 this question and my problem was solved similar to this answer 类似的问题,但我不明白是什么导致了这个问题。
我在 app.h
.
中声明了三个结构 File
、Line
和 Buffer
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 *buffer
在proto.h
中声明,在global.c
中定义,*buffer
在[=中动态分配23=]。 io.c
中有几个函数与 *buffer
一起使用。我将 io.c
的 show_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
并重新编译整个项目时,一切恢复正常。
我遇到了与 this question and my problem was solved similar to this answer 类似的问题,但我不明白是什么导致了这个问题。
我在 app.h
.
File
、Line
和 Buffer
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 *buffer
在proto.h
中声明,在global.c
中定义,*buffer
在[=中动态分配23=]。 io.c
中有几个函数与 *buffer
一起使用。我将 io.c
的 show_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
并重新编译整个项目时,一切恢复正常。