2

只是想知道fread()从 C 中的文件分配内存和数组数据的最合理方法是什么。

首先,一个解释:

int32_t longBuffer;

现在,当在 longBuffer 中进行 freading 时,代码可以如下所示:

fread(&longBuffer, sizeof(longBuffer), 1, fd); //version 1
fread(&longBuffer, sizeof(int32_t), 1, fd); //version 2

在这两者中,我会说版本 1 更安全,因为如果类型发生longBuffer变化(比如说 to int16_t),人们不必担心忘记用新类型更新fread()'s 。sizeof()

现在,对于一个数据数组,代码可以写成:

//listing 1
int8_t *charpBuffer=NULL; //line 1
charpBuffer = calloc(len, sizeof(int8_t)); //line 2
fread(charpBuffer, sizeof(int8_t), len, fd); //line 3

然而,这展示了第一个示例中暴露的问题:人们必须担心sizeof(<type>)在更改类型时不会忘记同步指令charpBuffer(比方说,从int8_t*to int16_t*)。

所以,有人可能会尝试写:

fread(charpBuffer, sizeof(charpBuffer[0]), len, fd); //line 3a

作为一个更安全的版本。这应该有效,因为在第 2 行分配之后,写入charpBuffer[0]是完全有效的。

此外,还可以写:

fread(charpBuffer, sizeof(*charpBuffer), len, fd); //line 3b

但是,尝试对内存分配做同样的事情,例如:

charpBuffer = calloc(len, sizeof(charpBuffer[0])); //line 2a

虽然语法更好,但表现出未定义的行为,因为在这个阶段,将charpBuffer[0]结果写入取消引用 NULL 指针。另外,写作:

charpBuffer = calloc(len, sizeof(*charpBuffer)); //line 2b

表现出同样的问题。

所以,现在的问题:

  1. 代码行“line 2b”和“line 3b”是否正确(忽略此问题的未定义行为)或者我错过了一些技巧,例如“line 2a/3a”和“line 2” /3”?

  2. 编写“清单 1”代码但避免任何形式的未定义行为的最安全的方式是什么?

编辑(为了澄清某些方面):

讨论的方向错了。编译时间与运行时间的问题是一回事(我也想对此有一个标准的保证,但这不是主题)。sizeof(NULL dereferencing) 的未定义行为问题是另一个问题。即使在编译时,我也不相信标准保证不会导致 UB。该标准是否提供任何保证?

4

2 回答 2

3

您似乎对sizeof运算符有错误的想法。此运算符在编译时进行评估,因此您传递给它的表达式在程序运行时没有机会被评估。

sizeof操作符的上下文中,无论它们是在相应的内存可用之前还是之后使用,它们都是安全的*charBuffercharBuffer[0]这只是一种避免键入类型名称的方法,因此减少了重复。

编辑

如下所述,sizeof在编译时评估的规则有一个明显的例外(尽管它与问题中发布的代码无关)。由于 C 和 C++ 允许可变长度数组作为自动变量,因此应用sizeof这些数组实际上可能会涉及一些运行时开销。

关于您对未定义行为的恐惧,我认为没有理由这样做,因为:

int vla[n]; // declare a variable-length array of length n

/* The compiler will produce code using the value of n prior to
   declaring the array to compute its size. */
x = sizeof(vla);

/* The space for the array is already available, so the expression 
   *vla is not UB anywhere (except if n is 0). Furthermore, n is 
   not involved in the computation and the operator can be evaluated at 
   compile-time. */
y = sizeof(*vla);

z = sizeof(vla[0]); // same thing
于 2013-02-28T09:05:19.257 回答
2

从 C99 6.5.4.3.2(强调我的):

运算符产生其sizeof操作数的大小(以字节为单位),它可以是表达式或带括号的类型名称。大小由操作数的类型决定。结果是一个整数。 如果操作数的类型是变长数组类型,则计算操作数;否则,不计算操作数,结果是一个整数常量。

操作数“未评估”意味着访问sizeof(charBuffer[0])or是完全安全的sizeof(*charBuffer),因为这些表达式仅用于它们的类型。同一页上的示例 3 继续明确记录sizeof array / sizeof array[0]用于计算数组中元素数量的习惯用法,但没有提及或暗示它对空数组无效。

于 2013-02-28T10:21:13.243 回答