在我的环境中,以下代码运行良好,但是否保证没问题?
FILE *file = fopen("in", "r");
int *read_to = NULL;
size_t count = 0;
size_t read = fread(read_to, sizeof(*read_to), count, file);
fclose(file);
分别
FILE *file = fopen("out", "w");
int *write = NULL;
size_t count = 0;
size_t written = fwrite(write, sizeof(*write), count, file);
fclose(file);
这不能保证有效,严格阅读标准会导致这样的结论:将空指针作为fread()
或fwrite()
的第一个参数传递会导致未定义的行为。
根据 C11 标准草案的 §7.21.8.1 ¶2:
size_t 胎面(无效 * 限制 PTR, size_t尺寸,size_t nmemb, 文件 * 限制流(;
fread函数读取ptr指向的数组。
同样,在 §7.21.8.2 ¶2 中:
size_t fwrite(常量无效 * 限制 PTR, size_t尺寸,size_t nmemb, 文件 * 限制流(;
fwrite函数从ptr指向的数组写入。
但是,在描述库函数使用(§7.1.4(的部分中指出:
如果函数的参数具有无效值(例如函数域外的值,或程序地址空间外的指针,或空指针,或指向不可修改存储的指针(当相应参数未限定 const 限定时(或具有可变参数数的函数不需要的类型(升级后(, 行为未定义。如果函数参数被描述为数组,则实际传递给函数的指针应具有一个值,该值应使所有地址计算和对对象的访问(如果指针确实指向此类数组的第一个元素,则有效(实际上是有效的。
最后,在 §4 ¶2 中:
如果违反了出现在约束或运行时约束之外的"应"或"不应"要求,则行为未定义。
由于空指针不指向有效对象,因此地址计算和访问在空指针上无效,因此将空指针传递给fread()
或fwrite()
违反了 §7.1.4 的"shall",因此行为是未定义的。
"N1570 委员会草案 — 2011 年 4 月 12 日 ISO/IEC 9899:201x"在 7.21.8 中说
如果大小或 nmemb 为零, fread 返回零,数组的内容和流的状态保持不变 变。
和
如果大小或 nmemb 为零, fwrite 返回零,流的状态保持不变。
根据上述文本,我希望它是有效的。
行为是否未定义取决于空参数是否被视为"无效"。 上面对行为的描述完全描述了大小为零时fread
和fwrite
的行为,忽略了数据指针:fread(anything, 0,0, anything)
的功能是返回零而没有副作用 - 该操作应该可以在不查看传入指针的情况下执行。
唯一的危险是,一些编译器编写者可能无法识别 7.1.4 中的"例如"语言,因为它提供了错误程序可能以请求函数执行"不可能"操作的方式传递的内容示例(例如从空地址读取或写入数据字节,或取消引用空指针或写入 const 限定的存储(, 而是将列表视为规范性规范,表明即使在自然会被忽略的上下文中,这些参数也是无效的。 虽然后一种处理是否会在人为场景之外提供任何有意义的性能优势是值得怀疑的,但认为"聪明"和"愚蠢"是反义词的编译器编写者可能会使用后一种处理来证明"优化"的合理性。
高质量的实现会将 fread(any,0,0,file( 视为无操作,而不考虑数据指针是否为 null,因为这样做将以零成本提供一些好处。
至于使用 fwrite 写入各种数据的代码,包括由 pointer+size_t 组合标识的可选块,以及哪些文档向其调用者记录 (null+0( 是指示没有可选数据的有效方式,是否应该对大小为零的情况使用特殊处理,我想这取决于一个人的代码是由高质量的编译器还是"聪明"的编译器处理。