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;
};