Search code examples
cmallocfreadcalloc

Most portable ways of allocating memory and reading arrays with fread() in C


Just wondering what is the soundest way to allocate memory and fread() array data from a file in C.

First, an explanation:

int32_t longBuffer;

Now, when freading in the longBuffer, the code could go as:

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

Among the two, I would say that version 1 is more bug-safe, since if the type of longBuffer changes (let's say to int16_t), one does not have to worry about forgetting to update the fread()'s sizeof() with the new type.

Now, for an array of data, the code could be written as:

//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

However, this exhibits the problem exposed in the first example: one has to worry about not forgetting to synchronize the sizeof(<type>) instructions when changing the type of charpBuffer (let's say, from int8_t* to int16_t*).

So, one may attempt to write:

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

as a more bug-safe version. This should work since, after the allocation on line 2, writing charpBuffer[0] is perfectly valid.

Also, one could write:

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

However, trying to do the same for memory allocation, such as:

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

while better in syntax, exhibits undefined behaviour because, at this stage, writing charpBuffer[0] results into dereferencing a NULL pointer. Also, writing:

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

exhibits the same problem.

So, now the questions:

  1. Are the lines of code "line 2b" and "line 3b" correct (ignore the undefined behaviour for this question) or there are some tricks that I miss w.r.t. their "wiser" counterparts such as "line 2a/3a" and "line 2/3"?

  2. What would be the most bug-safe way to write the "listing 1" code, but avoiding any form of undefined behaviour?

EDITS (in order to clarify some aspects):

The discussion took wrong direction. The question of compile time vs run time is one thing (and I would like to have a standard guarantee for this one, too, but it is not the topic). And the question of undefined behaviour for sizeof(NULL dereferencing) is another. Even if at compile time, I am not convinced that this is guaranteed by the standard to not result in UB. Does the standard provide any guarantees?


Solution

  • From C99 6.5.4.3.2 (emphasis mine):

    The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.

    That the operand is "not evaluated" means that it's perfectly safe to access sizeof(charBuffer[0]) or sizeof(*charBuffer), because those expressions are only used for their types. Example 3 on the same page goes on to explicitly document the sizeof array / sizeof array[0] idiom for computing the number of elements in an array without any mention or implication that it wouldn't be valid for empty arrays.