According to the standard (C17 draft, 7.22.3.2), calloc
void *calloc(size_t nmemb, size_t size);
"allocates space for an array of nmemb
objects, each of whose size is size
" (and initializes all bits to zero).
For calloc
ed arrays of T
, I have only ever seen code like this:
T *p = calloc(nmemb, sizeof(T));
T *p;
p = calloc(nmemb, sizeof(T));
But given that calloc
allocates space for an array, the following ought to be fine too:
T (*arrp)[nmemb] = calloc(nmemb, sizeof(T));
T (*arrp)[nmemb];
arrp = calloc(nmemb, sizeof(T));
(Here, the top version of each pair is technically speaking an initialization, not an assignment.)
What type can the result of calloc
be assigned to, a pointer to an array (type T (*)[]
), a pointer to the type contained within the array (type T *
), or either?
The following code
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int (*iarrp)[5];
int *ip;
float (*farrp)[1];
float *fp;
int i;
iarrp = calloc(5, sizeof(int));
for (i = 0; i < 5; ++i)
(*iarrp)[i] = -i;
ip = calloc(5, sizeof(int));
for (i = 0; i < 5; ++i)
ip[i] = i + 100;
for (i = 0; i < 5; ++i)
printf("%d: %d, %d\n", i, (*iarrp)[i], ip[i]);
farrp = calloc(1, sizeof(float));
(*farrp)[0] = 5.5;
fp = calloc(1, sizeof(float));
*fp = 6.6;
printf("%.2f, %.2f\n", (*farrp)[0], *fp);
free(iarrp);
free(ip);
free(farrp);
free(fp);
return 0;
}
compiles just fine for me with GCC (gcc -std=c17 -pedantic -Wall -Wextra
) and with MSVC (cl /std:c17 /Wall
), with the output being as expected:
0: 0, 100
1: -1, 101
2: -2, 102
3: -3, 103
4: -4, 104
5.50, 6.60
The background for asking this question is this: For an array arr
of type T[]
, of the following three expressions
arr
; type: T[]
(before decay), T *
(after decay)&arr[0]
; type: T *
arr
decays to&arr
; type: T (*)[]
&
prevents decaythe first two produce the same value. The third expression can theoretically have a value different from the first two expressions, though I understand that this is uncommon. The standard only guarantees that (void *)&arr == (void *)&arr[0]
holds true.
You ask:
What type can the result of calloc be assigned to
The answer can be found in the standard (e.g. C17 7.22.3):
The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object
That's it. Any pointer type.
Then you say:
... the following ought to be fine too:
T (*arrp)[nmemb] = calloc(nmemb, sizeof(T));
Well... yes, as long as you access the memory like (*arrp)[i]
or arrp[0][i]
(and 0 <= i < nmemb
), it will work.
But it's not really how calloc
is intended to be used. Your code tells calloc
that each element has the size sizeof(T)
. However, you store the return value into a pointer that points to an element with size nmemb * sizeof(T)
. This is because your arrp
is a pointer to an array of nmemb
Ts.
For your code the intended form would be:
T (*arrp)[nmemb] = calloc(1, nmemb * sizeof(T));
A better way of writing this would be:
T (*arrp)[nmemb] = calloc(1, sizeof *arrp);
This brings to the "normal" use of this code... It's for allocation of 2 dimensional arrays (aka "arrays-of-arrays). Like this:
T (*arrp)[num_columns] = calloc(num_rows, sizeof *arrp);
^ ^
\-------------------/
notice: Unlike your example these values are not the same
This code gives you a dynamic allocation that corresponds to the static/automatic allocation:
T arr[num_rows][num_columns];
BTW:
This could be an interresting read: