I took your code and added also a 4th alternative. And I will post a program and discussion that can be useful on making this a bit more clear.
I believe that this line
Node* test1 = malloc(_SIZE_ * sizeof(Node));
is key to understand how things can get confused. malloc() returns just a pointer to an area of the size of the argument. In fact these 2 lines are similar
int* example = malloc(511);
Node* test1 = malloc(_SIZE_ * sizeof(Node));
and it illustrates why people from C++ make it mandatory to cast a type for the return of malloc() like in
int* example = (int*) malloc(511);
Node* test1 = (Node*) malloc(_SIZE_ * sizeof(Node));
it makes things clearer, they said. And I believe. This way we see that test1 is just a pointer to Node and it can warn us that maybe things are going wrong, or may be not as we expected: it makes no difference the number of bytes allocated, it will be a NODE*. A pointer to an area.
back to the test[123] stuff here
test1 as
Node* test1 = malloc(_SIZE_ * sizeof(Node));
test1 is just a pointer to Node. malloc() will happily assign how many bytes as it evaluates from the argument. Even less than the size of one Node, and the program may crash real fast... or 511 bytes, making no practical difference in the test but bringing it to this topic in SO :)
test
#define _SIZE_ 16
Node test[_SIZE_];
test is just an array of Node
typedef struct node
{
int number;
struct node* left;
struct node* right;
} Node;
test2
Node (*test2)[_SIZE_] = malloc(_SIZE_ * sizeof(Node));
This is not frequently seen because it is not flexible: test2 is a pointer to an array of [_SIZE_] elements of Node. A thing just like test. In fact I will show below that is perfectly ok to write
Node test[_SIZE_];
Node (*test2)[_SIZE_] = &test;
because this is just the definition of the thing test2 points to.But as the _SIZE_ must the known at compile time it is rarely used. Instead we have things much more flexible like the familiar
int main(int argc, char** argv);
And introducing test3
Node** test3;
Here test3 is a pointer to an array of pointers to Node, and this is the useful way, as every C or C++ or any program knows about.
Let us fill it in
Node** test3 = (Node**)malloc(sizeof(Node*) * _SIZE_);
for (int i = 0; i < _SIZE_; i += 1)
{
test3[i] = (Node*)malloc(sizeof(Node));
test3[i]->number = 1000 + i;
};
Now test3 points to an area of _SIZE_ times the sizeof() of a pointer to NODE. And we go into the area and set up the individual pointers to a real NODE, each and every one. And we put a value into the number member of each Node so we can print it later in the example program.
- What is the difference? Now we can iterate over the Nodes just like we do over and over again on
argv[i]
- What is missing? The size information. This is why we have
argc in every program. We could write
// now to iterate over Nodes: should be as familiar as
typedef struct
{
int nodec;
Node** nodev;
} NodeArray;
so familiar...
And we can pass over NodeArrays, iterable arrays of structures, just like the command line arguments...
output of the example
sizeof(test) = 384
sizeof(test1) = 8
sizeof(test2) = 8
test is Node[_SIZE_]. Values are
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
test2 is a pointer to Node[_SIZE_]. So we can assign &test to it
Done. Now the values of test2:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
test2 restored. Now set up from 500
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
test1 is just a pointer to Node. Let's set it to 300
*test1 is 300
test3 is an array of pointers to Node, set up from 1000:
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
Sample code
#define _SIZE_ 16
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
int number;
struct node* left;
struct node* right;
} Node;
int main(void)
{
Node test[_SIZE_];
Node* test1 = malloc(_SIZE_ * sizeof(Node));
int* example = malloc(511); // no meaning
Node (*test2)[_SIZE_] = malloc(_SIZE_ * sizeof(Node));
// test2 points to Node[_SIZE_]
for (int i = 0; i < _SIZE_; i += 1) test[i].number = i;
printf("sizeof(test) = %zd\n", sizeof(test));
printf("sizeof(test1) = %zd\n", sizeof(test1));
printf("sizeof(test2) = %zd\n", sizeof(test2));
// test is an array of Node
printf("\ntest is Node[_SIZE_]. Values are \n");
for (int i = 0; i < _SIZE_; i += 1)
printf("%6d", test[i].number);
printf("\n");
// test2 points to an array of Node
printf("\ntest2 is a pointer to Node[_SIZE_]. So we can assign &test to it\n");
void* save = test2; // or it will leak
test2 = &test;
printf("\nDone. Now the values of test2:\n");
for (int i = 0; i < _SIZE_; i += 1)
printf("%6d", (*test2)[i].number);
printf("\n");
test2 = save; // restored
printf("\ntest2 restored. Now set up from 500\n");
for (int i = 0; i < _SIZE_; i += 1) (*test2)[i].number = 500 + i;
for (int i = 0; i < _SIZE_; i += 1)
printf("%6d", (*test2)[i].number);
printf("\n");
// test1 is just a pointer to node
printf("\ntest1 is just a pointer to Node. Let's set it to 300\n");
test1->number = 300;
printf("*test1 is %d\n", test1->number);
// now to iterate over Nodes: should be as familiar as
typedef struct
{
int nodec;
Node** nodev;
} NodeArray;
//Node** test3;
Node** test3 = (Node**)malloc(sizeof(Node*) * _SIZE_);
for (int i = 0; i < _SIZE_; i += 1)
{
test3[i] = (Node*)malloc(sizeof(Node));
test3[i]->number = 1000 + i;
};
// test3 is an array of Node
printf("\ntest3 is an array of pointers to Node, set up from 1000:\n");
for (int i = 0; i < _SIZE_; i += 1)
printf("%6d", test3[i]->number);
printf("\n");
// now free() all this
// test is static
free(test1); // test1 is Node*
// test2 is Node (*)[]
free(test2);
// test3 is a pointer to an array of pointers...
for (int i = 0; i < _SIZE_; i += 1) free(test3[i]);
// all gone
test3 = NULL; // invalidate it
printf("\n");
return 0;
};