struct epoll_event memset 还是没有 memset?

struct epoll_event memset or no memset?

在互联网上浏览代码时,我经常看到这样的片段:

struct epoll_event event;
memset(&event, 0, sizeof(event));

如果事件填写完整,这种模式对我来说似乎是不必要的,但它很普遍。也许考虑到结构的未来可能变化?

这肯定是糟糕的复制粘贴编码。 The man page for epoll 没有记录对 epoll_event 结构进行零初始化的任何需要,并且在示例中也没有这样做。未来对该结构的更改似乎是不可能的(ABI),但如果是的话,合同显然会忽略与您请求的 events 无关的结构的任何部分(甚至不会阅读,因为调用者可能正在传递指向未超出原始定义的存储的指针)。

此外,一般来说,当结构应该被零初始化时,使用 memset 充其量是毫无意义的,最坏的情况是 incorrect/nonportable,因为零表示不一定是零值(对于指针和浮点类型)。如今,这种普遍性主要是出于历史的好奇心,与 Linux 特定的接口(如 epoll 无关),但它也出现在 mbstate_t 中,它存在于完全通用的 C 中,并且其中需要零初始化才能正确使用关联的接口。对需要零值而不是全零字节表示的事物进行零初始化的正确方法是使用通用零初始化程序 { 0 }.

像这样使用memset可以帮助您更快地定位bug。将其视为一种防御性(甚至是安全的)编程风格。

假设您没有使用 memset,而是尝试认真填写 API 记录的每个成员。但是,如果您忘记填写一个字段(或者后来的 API 更改导致添加了一个新字段),那么该字段在 运行 时间采用的值是未定义的;并且在实践中将使用之前保存的任何内存。

有什么后果?

如果幸运的话,您的代码将立即以一种可以调试的良好方式失败,例如,如果未设置的字段需要一个非常具体的值。

如果你运气不好,你的代码可能仍然可以工作,而且可能工作多年。也许在您当前的操作系统上,程序内存不知何故已经保存了 API 所期望的正确值。但是,当您跨系统和编译器移动代码时,会出现令人困惑的行为:"it works on my machine, but I don't understand why it doesn't work on yours"。

所以在这种情况下,memset 可以帮助您避免这种不确定的行为。

当然,您仍然可以分析您的代码、检查未定义的内存、单元测试等。执行 memset 并不能替代这些。这只是获得安全软件的另一种技术。