在C语言中使用fread()分配内存和读取数组的大多数可移植方法

时间:2013-02-28 09:01:30

标签: c malloc fread calloc

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

首先,解释:

int32_t longBuffer;

现在,当在longBuffer中进行游说时,代码可以是:

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

在这两者中,我会说版本1更安全,因为如果longBuffer的类型改变(让我们说int16_t),则不必担心忘记使用新类型更新fread()的{​​{1}}。

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

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>)指令(比方说,charpBufferint8_t*)。

所以,有人可能会写:

int16_t*

作为更安全的错误版本。这应该有效,因为在第2行分配之后,写fread(charpBuffer, sizeof(charpBuffer[0]), len, fd); //line 3a 完全有效。

另外,有人可以写:

charpBuffer[0]

但是,尝试对内存分配执行相同操作,例如:

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

虽然语法更好,但表现出未定义的行为,因为在此阶段,将charpBuffer = calloc(len, sizeof(charpBuffer[0])); //line 2a 结果写入取消引用NULL指针。另外,写作:

charpBuffer[0]

表现出同样的问题。

所以,现在问题:

  1. 代码行“2b”和“3b”是否正确(忽略此问题的未定义行为)或者有一些我想念的技巧w.r.t.他们的“明智”对应物如“2a / 3a”和“2/3行”?

  2. 编写“清单1”代码最容易出错的方法是什么,但避免任何形式的未定义行为?

  3. EDITS(为了澄清某些方面):

    讨论采取了错误的方向。编译时间与运行时间的问题是一回事(我也希望对这个问题有一个标准的保证,但这不是主题)。而对于sizeof(NULL解除引用)的未定义行为的问题是另一个问题。即使在编译时,我也不相信标准可以保证不会导致UB。标准是否提供任何保证?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:2)

从C99 6.5.4.3.2(强调我的):

  

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

操作数“未评估”意味着访问sizeof(charBuffer[0])sizeof(*charBuffer)非常安全,因为这些表达式仅用于其类型。同一页面上的示例3继续显式地记录sizeof array / sizeof array[0]用于计算数组中元素数量的习惯用语,而没有任何提及或暗示它对空数组无效。