How is a C program able to determine the size of an array?

144 Views Asked by At

Because an "array" or an array name is simply a pointer that points to the "start point" of a block of data, how does the sizeof operator return the size of an array? I know that this is determined at run time.

int arr[10]; //Allocate enough memory to store 10 ints and make arr a pointer to the first address.

This question arises because we cannot determine the size of a malloc-ed chunk of memory using the operator. My understanding is that a "normal" array is same as malloc-ed memory, except for the use of the heap.

I am not able to understand why a non-heap array and heap array behave differently.

2

There are 2 best solutions below

0
Surge On

While an array name is used as a pointer in pointer arithmetic and when it is a function argument, an array is not a pointer. It is its own type. Loosely speaking, the sizeof operator cannot tell you the size of an object you would not know at compiler time. (See the note on VLAs at the end.) The size of a C array like in

...
int arr[10];
...

is known at compile time. The sizeof operator can report the size of arr because arr is set as a contiguous chunk of ten integers. It is part of its 'type', again, loosely speaking of 'type'.

When you malloc an array, what you get is a pointer to a yet-to-be determined chunk of heap memory. You cannot obviously know the size without analyzing the parameter of malloc at compile time. The only way the compiler could do that would be to deeply analyze your program and figure it out. Often, that's completely impossible of course, because dynamic allocation will depend on the user.

VLAs are a special case. The sizeof operator will calculate the size at runtime (C99 and later I believe). There are futher caveats that you can read about here.

0
Eric Postpischil On

Because an "array" or an array name is simply a pointer that points to the "start point" of a block of data,…

That premise is false. The name of an array designates the array.

For a fixed-length array, in sizeof array, the compiler knows the size of the array and uses that as the value of the expression. For a variable-length array, the size will be determined during program execution, and the compiler will generate code that uses that size as the value of the expression.

When an array is used in an expression other than as the operand of sizeof, as the operand of unary &, or as a string literal used to initialize an array, it is automatically converted to a pointer. This means an array may be used as a pointer in other contexts. For example:

  • array[i] converts array to a pointer, performs address arithmetic to add i elements to it, and dereferences the resulting address.
  • array + i converts array to a pointer and performs address arithmetic to produce a pointer to element i of the array.
  • foo(array); converts array to a pointer and passes that pointer to the function foo.

Also, when a function parameter is declared as an array, the declaration is automatically adjusted to declare a pointer instead.

My understanding is that a "normal" array is same as malloc-ed memory, except for the use of the heap.

An array is an array wherever and whenever it appears. However, with your so-called “normal” array, you have a name for it. int array[10]; defines an array (including reserving memory for it) and declares array to be the name for it. int *p = malloc(10 * sizeof *p); also reserve enough memory for an array, and you can use that memory to store an array, but you do not have a name for it. p is not a name for the array; it is just a pointer to the memory.

In sizeof array, the operand of sizeof is an array because array names the array. So sizeof produces the size of the array. In sizeof p, the operand of sizeof is a pointer, not an array. So sizeof p produces the size of a pointer.

You could create an lvalue that designates an array in dynamically allocated memory this way:

void *p = malloc(10 * sizeof (int));
int (*pA)[10] = p;   // Make a pointer to an array at p.
int *pE = &(*pA)[0]; // Make a pointer to the first element of pA.

Now pA points to the array (the “same” value as a pointer to the first element, but its type is different), so *pA designates the array. sizeof *pA will produce the size of the array. And you can access the array elements with (*pA)[i]. But it is more convenient to use pE[i], which is the same thing.

Also note that int *pE = *pA; would be equivalent to the above definition of pE. *pA designates the array, so it is automatically converted to a pointer to its first element, which is &(*pA)[0]. Or we could use int *pE = p;.