0

Why can't I properly store and reference a double pointer?

This code works:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct _node{
    int nodeNumber;
    int weight;
}* Node;

int main(int argc, char **argv){
    Node nodeList = calloc(3, sizeof(struct _node));

    // Used for testing
    nodeList->nodeNumber = 9;
    printf("Node Number: %d\n", nodeList->nodeNumber);

    return 0;
}

but when I try making the struct a double pointer, and referencing like this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct _node{
        int nodeNumber;
        int weight;
}** Node;



int main(int argc, char **argv){
        Node nodeList = calloc(3, sizeof(struct _node));

        // Used for testing
        nodeList[0]->nodeNumber = 9;
        printf("Node Number: %d\n", nodeList[0]->nodeNumber);

        return 0;
}

My program runs for a second then crashes. No error or anything. I thought that referencing the struct with

nodeList[0]->nodeNumber = 9;

would work but apparently it does not.

Also I would like to note that I know creating a pointer or double pointer directly in the struct is usually considered bad practice but this is part of an assignment and the struct definition was given and must be used "as is". The ultimate goal is to make an array or linked lists. The linked lists part will be find as I think I understand that but this is the problem.

------------------------------- EDIT ------------------------------------

I have changed my code to this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct _node{
        int nodeNumber;
        int weight;
}** Node;



int main(int argc, char **argv){
        Node nodeList = malloc(sizeof(struct _node *));

        // Used for testing
        nodeList[0]->nodeNumber = 9;
        printf("Node Number: %d\n", nodeList[0]->nodeNumber);

        return 0;
}

but my program is still crashing.

3 Answers3

1

If you insist on using double pointers, then you should proceed as follows:

typedef struct _node{
    int nodeNumber;
    int weight;
} Node;

and in your function:

    Node **nodelist = malloc(3 * sizeof(Node *));
    for (int i=0; i<3; i++)
        nodelist[i]= calloc(1,sizeof(Node));

Note that I don't use pointers in the typedef because it is very confusing. Instead, I declare the nodelist as a double pointer.


So you professor insists on using a double pointer in the typedef (I suggest you tell your professor to visit stackoverflow.com....). Then proceed as follows:
typedef struct _node{
    int nodeNumber;
    int weight;
} **Node;

and in your function:

    Node nodelist = malloc(3 * sizeof(*nodelist));
    for (int i=0; i<3; i++)
        nodelist[i]= calloc(1,sizeof(*nodelist[i]));

Here I don't use the typename but the variable name to determine the size to allocate: the *nodelist dereferences the nodelist to a struct _node * and the *nodelist[i] dereferences that to the actual struct _node (note that the value of i is not important here; it is only used to indicate to the compler that an array element is intended).

People prefer it even to use the variable name instead of the type name so when in future the variable would refer to another type, the allocation changes automatically with it.

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • I appreciate your help, but like I said above I have to use the code "AS-IS". My professor insists on using the double pointers in the typedef declaration. I made a working program another way, but unfortunately I am forced to do it this way. I agree it is very confusing and silly, however I have to adhere to the specs of the assignment. – I_am_learning_now Jan 22 '19 at 09:32
  • Thanks a lot Paul! It works! I tried initializing it myself a different way but it wasn't quite working but it appears your way does! Thank you for your patience! – I_am_learning_now Jan 22 '19 at 09:47
0

With

typedef struct _node{
    int nodeNumber;
    int weight;
}* Node;

and

Node nodeList = calloc(3, sizeof(struct _node));

you make nodeList point to the first element of an array of struct _node elements.

With

typedef struct _node{
        int nodeNumber;
        int weight;
}** Node;

then nodeList will be a pointer to the first element of an array of pointers to struct _node, but since you use calloc all those pointers in the array will be NULL. Dereferencing a NULL pointer is invalid and leads to undefined behavior.

Furthermore your allocation is wrong since you still allocate memory for three struct _node elements instead of struct _node * elements.

And as a rule of thumb: Never hide pointers behind type-aliases. It makes code harder to read, understand and maintain.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • @I_am_learning_now `nodeList[0]` is a pointer, but ***where does it point***? Through your use of `calloc` all pointers in the array will be `NULL`. – Some programmer dude Jan 22 '19 at 09:17
  • Ah ok so I should probably use malloc instead and change my allocation to a pointer is what I'm gathering. I will try that and see if it works – I_am_learning_now Jan 22 '19 at 09:22
  • Ok so when I changed my code, I am still getting the same problem. Check my edit above for new code. however the program is still crashing. Any suggestions? – I_am_learning_now Jan 22 '19 at 09:23
  • @I_am_learning_now Using `malloc` doesn't change anything, and in fact I would argue making it worse. Now you allocate an array of pointers, but each pointer is *indeterminate*. The pointers in the array will not automatically be initialized and point to nodes. You need to do that yourself. Like `nodeList[0] = malloc(sizeof(struct _node));`. – Some programmer dude Jan 22 '19 at 09:27
  • Ah k I think that's my problem! Sorry I am not confident with C. I will try initializing them and see what happens. – I_am_learning_now Jan 22 '19 at 09:30
0

Others have answered the question, but in case someone comes across this and is interested how to do this proper:

  • Never hide pointers behind typedefs.
  • Never use type** when you actually want a 2D array. That should only be used for things like a table of strings of variable length, which isn't a 2D array. See Correctly allocating multi-dimensional arrays for details.
  • Always free() what you malloc(). Sure, the OS will do this for you in most cases. But by calling free(), we can expose and detect bugs elsewhere in the code, such as memory leaks, dangling pointers etc etc, which will all manifest themselves as a program crash upon calling free().

Here are examples of proper code, one example with a 1D array and one with a 2D array:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
  int nodeNumber;
  int weight;
} Node;

int main (void)
{
  Node* nodeList = calloc(3, sizeof *nodeList);

  nodeList[0].nodeNumber = 9;
  printf("Node Number: %d\n\n", nodeList->nodeNumber);

  free(nodeList);


  /****************************************************************************/

  const size_t x = 2;
  const size_t y = 3;
  Node (*nodeList2D)[y] = calloc(x, sizeof *nodeList2D);

  int count = 0;
  for(int i=0; i<x; i++)
  {
    for(int j=0; j<y; j++)
    {
      nodeList2D[i][j].nodeNumber = count++;
      printf("Node (%d, %d): %d\n", i, j, nodeList2D[i][j].nodeNumber);
    }
  }

  free(nodeList2D);

  return 0;
}

Note that the sizeof *nodeList2D trick when calling malloc/calloc is adaptive to the type used. In case of the 2D array, this will give us the size of one 1D array (same as 3 * sizeof(Node)), and then we allocate 2 such chunks of memory with calloc.

Lundin
  • 195,001
  • 40
  • 254
  • 396