1

Im trying to create a graph structure on C but I got some issues. First, Im getting 2 compilation errors:

main.c:18:19: error: member reference type 'node' is not a
      pointer; did you mean to use '.'?
      graph[index]->start = NULL;
      ~~~~~~~~~~~~^~
                  .
main.c:18:27: error: expression is not assignable
      graph[index]->start = NULL;
      ~~~~~~~~~~~~~~~~~~~ ^
2 errors generated.
compiler exit status 1

I cannot figure out what Im doing wrong. I tried to create an array of nodes* but the compiler doesn't recognize it as a pointer for some reason. It's like malloc doesn't work. Also, I can't manage to acess edge* fields because it's like the array of nodes* is non-existent.

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

#define maxNodes 4

typedef struct edge {
    int target;
    struct edge* next;
} edge;

typedef struct {
    edge* start;
} node;

void initializeGraph(node* graph) {
    graph = (node *) malloc(maxNodes * sizeof(node));
    for(int index = 0; index < maxNodes; index++) {
      graph[index]->start = NULL;
    }
}

int main(void) {
    node test;
    initializeGraph(&test);
}

Im trying to initialize my structure. Any help is appreciated.

  • What do you think the type of `graph[index]` is? Do you know what the difference between `.` and `->` is? – Joseph Sible-Reinstate Monica Mar 10 '20 at 03:20
  • You pass a *copy of* the pointer `graph` with `void initializeGraph(node* graph)`. The allocation in your function is never seen back in the calling function. You have 2 options (1) make the return type `node *` and return a pointer to the allocated block for assignment in the caller, or (2) make the parameter `node**` and pass the address. Also note `[..]` acts as a dereference, so `graph[index].start` would be proper. – David C. Rankin Mar 10 '20 at 03:21
  • And, you can't declare a node with *static storage duration* in `main()` and pass its address for allocation in your function. (bad things will happen). When you declare `node test;` all storage is already provided on the stack. You can declare a pointer and allocate (subject to the comment above), which if you intend to have more than 1-node is what you will need to do. – David C. Rankin Mar 10 '20 at 03:26
  • I've changed -> to . and fixed the static storage duration thing but I still cant use edge* test; test->target = 400; graph[3].start = test; //I get segmentation fault like I didnt allocate the memory before.... Any ideias? – padariadoce Mar 10 '20 at 03:38
  • When you allocate for `test` (as shown below in the answer), you allocate storage for 4 `struct node`. Each of which contain an *uninitialized pointer* of type `stuct edge` named `start`. You can set the `start` pointers `NULL` as you do in your function, but you cannot use them in any other way until your allocate storage and assign the beginning address for that block of memory to `test[i].start`. Then you can use `test[i].start`. – David C. Rankin Mar 10 '20 at 03:43
  • You need to study this: [Dynamic memory access only works inside function](https://stackoverflow.com/questions/39486797/dynamic-memory-access-only-works-inside-function). – Lundin Mar 10 '20 at 12:01

2 Answers2

1

You have a large number of problems in your short example code. As to your error, that is covered by @dbush's answer and [...] serves as a dereference on your pointer making the '.' (dot) operator proper instead of the -> arrow operator.

Next, you cannot declare a node with static storage duration in main() and pass its address for allocation in your function. When you declare node test; all storage is already provided on the stack. You can't then pass that address to your function and allocate additional memory for that struct.

If you intend to have more than one node, then you can either declare an array with static storage duration in main(), or you must declare a pointer in main() and allocate in your function. To make that allocation visible in main(), as noted in my comment, you can either (1) make the return type node * and return a pointer to the allocated block for assignment in the caller, or (2) make the parameter node** and pass the address of your pointer as the parameter.

Putting that altogether and choosing option (1) above, you could do:

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

#define maxNodes 4

typedef struct edge {
    int target;
    struct edge* next;
} edge;

typedef struct {
    edge* start;
} node;

node *initializeGraph (void) {
    node *graph = malloc(maxNodes * sizeof *graph);
    if (!graph)
        return NULL;
    for (int index = 0; index < maxNodes; index++) {
        graph[index].start = NULL;
    }
    return graph;
}

int main (void) {

    node *test = initializeGraph();

    if (!test)
        fputs ("error: initialization failed.\n", stderr);
    else
        puts ("initialization succeeded");
}

Example Use/Output

$ ./bin/graphinit
initialization succeeded

Allocating For Each test[i].start

Before you can make use of any of the start pointers, you must allocate storage for a struct edge and assign the beginning address for that block of memory to each of your test[i].start pointers. You can do that in your same initializeGraph() function by allocating where you currently set the pointers NULL, e.g.

node *initializeGraph (void)
{
    node *graph = malloc(maxNodes * sizeof *graph);
    if (!graph)
        return NULL;
    for (int index = 0; index < maxNodes; index++) {
        graph[index].start = malloc (sizeof *graph[index].start);
        if (!graph[index].start)
            return NULL;
    }
    return graph;
}

You can then assign a value to the target in each. Extending the earlier example, you could do:

int main (void) {

    node *test = initializeGraph();

    if (!test)
        fputs ("error: initialization failed.\n", stderr);
    else
        puts ("initialization succeeded");

    for (int i = 0; i < maxNodes; i++)
        test[i].start->target = i;

    puts ("targets filled");
}

Example Use/Output

$ ./bin/graphinit
initialization succeeded
targets filled

(don't forget to free the memory you allocate when it is no longer needed)

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thanks for helping me. I understood all the points you made and now the code is working. I didn't know C had so much details to care about. I'll be more careful. – padariadoce Mar 10 '20 at 03:45
  • You have to put your accountants hat on and account for each byte of memory you use or allocate. Hold on a sec and I'll drop an example showing you how to allocate for and fill `targets`. – David C. Rankin Mar 10 '20 at 03:48
  • Ok. I would appreciate that! – padariadoce Mar 10 '20 at 03:54
  • Hit refresh -- I added the example at the end. – David C. Rankin Mar 10 '20 at 03:54
  • Basically whenever I want to use pointers I must previously allocate memory with malloc and then assign an initial adress value to this chunk of memory. Is that right? Im used to Java where all the memory thing is handled by the language, so sorry if it sounds like a stupid question. – padariadoce Mar 10 '20 at 04:02
  • YES. A pointer is uninitialized -- until it points (holds the address of) a valid block of memory. You can allocate that memory, or you can assign the address of something that is already allocated -- but it must be one or the other. You have to `free` the memory in the reverse order allocated. Above you will have to loop `0 <= i < maxNodes` and `free (test[i].start` before freeing the pointers with `free (test)`; – David C. Rankin Mar 10 '20 at 04:38
0

The array index operator [] implicitly dereferences a pointer. The syntax a[b] is exactly the same as *(a + b).

This means that graph[index] has type node, not node *. So use . instead of -> as the error message suggests.

graph[index].start = NULL;
dbush
  • 205,898
  • 23
  • 218
  • 273