16
votes

There is a well known pattern of figuring out array length:

int arr[10]; 
size_t len = sizeof(arr) / sizeof(arr[0]); 
assert(len == 10); 

This pattern applies to static arrays and auto arrays of constant size. It also applies to variable length arrays in C99.

I want to apply similar idea for figuring out dynamic array size in bytes:

size_t known_len = 10; 
int *ptr = malloc(known_len * sizeof(int)); 
size_t size = known_len * sizeof(ptr[0]); 
assert(size == known_len * sizeof(int)); 

This is nicer than known_len * sizeof(int) because sizeof(ptr[0]) doesn't refer to actual array element type. Hence it doesn't require reader of the code to know the type.

However it is unclear to me whether expression sizeof(ptr[0]) can lead to undefined behavior. As it is expanded:

sizeof(ptr[0]) -> sizeof(*((ptr) + (0))) -> sizeof(*ptr) 

The result expression is questionable in case if ptr is 0:

sizeof(*((int*) 0)) 

According to a C99 standard:

(C99, 6.3.2.3p3 ): "An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant." Dereferencing a null pointer is undefined behavior.

(C99, 6.5.3.2.p4) "If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.87)"

87): "Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime."

But it's never specified whether sizeof of such expression can lead to undefined behavior. In fact such sizeof should be evaluated at compile time.

My questions are:

  • Can the expression sizeof(ptr[0]) be used in the code when type of ptr is known and the value of ptr is not known?
  • Can such use be justified according to C99 standard? GNU GCC spec?
5
I have added an extra answer to the linked question. My concern here is about situations when arr pointer is declared as, for example, int n = 10; int (*arr)[n] = NULL;, i.e. it is a pointer to a VLA. In C language the argument of sizeof is evaluated at run-time if it has VLA type (!). The question in this case is whether sizeof arr[0] is supposed to produce undefined behavior. Most likely it is.AnT
In fact, the linked question seems to be restricted to non-VLA contexts, while this question does not immediately have such restriction. This makes it a non-duplicate.AnT
As an aside, consider malloc(known_len * sizeof *ptr) instead of malloc(known_len * sizeof(int)). Should the type of ptr change, or obscured (buried deep in some .h file), this style is more often correct and easier to maintain.chux - Reinstate Monica

5 Answers

17
votes

The expression ptr[0] will not be evaluated in sizeof(ptr[0]). Size will be determined by just using the type of ptr[0] at compile time.

C11: 6.5.3.4:

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 aninteger constant.

That means, there is no undefined behavior.

11
votes

This will not cause undefined behavior.

With the exception of taking the size of variable-length arrays, sizeof is a compile-time constant expression. The compiler processes the expression to determine its type, without producing the code to evaluate the expression at compile time. Therefore, the value at ptr[0] (which is an uninitialized pointer) does not matter at all.

Moreover, if you would like to allocate ten integers, you should be calling malloc like this:

int *ptr = malloc(known_len * sizeof(ptr[0])); 

Otherwise, you allocate ten bytes, which is too small for storing ten integers. Note that in the expression above ptr is uninitialized at the time of the call, which is perfectly fine for sizeof.

2
votes

In general case, if I'm missing something, dereferencing a null pointer under sizeof can lead to undefined behavior. Since C99, sizeof is not a purely compile time construct. The operand of sizeof is evaluated at run-time if the operand type is a VLA.

Consider the following example

unsigned n = 10;
int (*a)[n] = NULL; // `a` is a pointer to a VLA 

unsigned i = 0;
sizeof a[i++];      // applying `sizeof` to a VLA

According to the C99 standard, the argument of sizeof is supposed to be evaluated (i.e. i is supposed to get incremented). Yet I'm not entirely sure that the null-point dereference in a[0] is supposed to produce undefined behavior here.

1
votes

As an expression (not a declaration), ptr[0] is identical to *ptr.

As long as the type of ptr is known and it is not pointing to void, sizeof is guaranteed to work on sizeof(*ptr) as well as sizeof(ptr[0]).

1
votes

The code is evil.

size_t known_len = 10; 
int *ptr = malloc(known_len); 
size_t size = known_len * sizeof(ptr[0]); 
assert(size == known_len * sizeof(int)); 

The size of the memory allocation from your view point is 10 bytes. That is not 10 pointers to int.

 known_len / sizeof(ptr[0]) 

will give you the number of integers that the allocation can hold.

As is, you are likely to go off the end of the allocation, leading to undefined behavior.

Consider malloc(known_length * sizeof ptr[0]);