1

a.c

#include "b.h"

typedef struct line_details {
    int line_num;
    char type[20];
} line_details;

line_details* ptr = NULL;
ptr = (*line_details) malloc(sizeof(line_details));

int x = 5;

do_something(x, &ptr);

b.c

void do_something(int n, line_details* ptr)
{
    /* some code */
}

b.h

void do_something(int n, line_details* ptr);

I want to pass to function do_something the address of the pointer and not just the copy of the pointer. When I do so, i get incompatible pointer type error. Why?

Losnikov
  • 87
  • 5
  • 2
    "Pointer by reference" in C translates to `**` pointers. – tadman Mar 02 '21 at 18:17
  • 2
    Why do you want to pass the pointer by reference? Do you want to change the pointer itself in `do_something`? – Werner Henze Mar 02 '21 at 18:19
  • 1
    @WernerHenze No. I want to change the struct's members – Losnikov Mar 02 '21 at 18:28
  • 2
    A regular pointer will suffice for that. – tadman Mar 02 '21 at 18:29
  • @WernerHenze And if I want to change the pointer itself too, how should i pass the pointer? – Losnikov Mar 02 '21 at 18:31
  • C doesn't have the concept of "passing by reference". There is only pass by value and pass by pointer. – Mooing Duck Mar 02 '21 at 18:34
  • @Losnikov If you just need to access the data in the struct, then pass the struct by value or by reference (`line_details l` or `line_details *pl`). If you need to change the pointer to the struct, for example because this is an allocation or free function, then pass the pointer by reference (`line_details **ppl`). – Werner Henze Mar 02 '21 at 18:37
  • Move `typedef struct line_details ...` to b.h – chux - Reinstate Monica Mar 02 '21 at 18:46
  • 1
    To answer the "why pass a pointer to a pointer" question - this can happen if you are working with multi-dimensional arrays or if you had a reason to change the actual pointer itself, say allocating memory and returning a new pointer. Going star crazy is not a good practice - see https://wiki.c2.com/?ThreeStarProgrammer – Michael Dorgan Mar 02 '21 at 18:55
  • @WernerHenze And if I want to pass the pointer by reference again from `do_something` to another `function`, for example, for allocation or free, what should be the signature of `function` and what to pass to `function`? – Losnikov Mar 02 '21 at 19:15
  • @Losnikov As said. If `function` shall change the pointer it needs `line_details ** ppl`. If `do_something` might call `function` then `do_something` requires `line_details ** ppl` so it can pass this `ppl` to `function`. I'll wrap that up in an answer. – Werner Henze Mar 02 '21 at 19:19
  • @WernerHenze So `do_something` passes `ppl` or `&ppl` to `function`? – Losnikov Mar 02 '21 at 19:27
  • @Losnikov Please see my answer for some sample code. – Werner Henze Mar 02 '21 at 19:29

3 Answers3

3
  1. Your cast to malloc is wrong, the correct cast is (line_details *). But that's if you even want to cast malloc in the first place.

  2. ptr is already a pointer. What you're passing with &ptr is the address of line_details *ptr, in other words, a pointer to a pointer. Just pass ptr.

  3. If your intent actually was to pass a pointer to ptr (i.e. a pointer to a pointer to a line_details), change the function signature to take line_details ** instead, and then pass &ptr.

Govind Parmar
  • 20,656
  • 7
  • 53
  • 85
1

Few issues in the code as below.

  • If you want to typecast the pointer returned by malloc, you have to tell the type of the pointer you want to typecast i.e. '''line_details*''' not '''*line_details'''

  • ptr itself is a pointer. Passing ptr to a function is enough. If you pass &ptr, it is not a pointer. It is address of pointer.

Modified program is as below.

#include "b.h"

typedef struct line_details {
    int line_num;
    char type[20];
} line_details;

line_details* ptr = NULL;
ptr = (line_details *) malloc(sizeof(line_details));

int x = 5;

do_something(x, ptr);

If you want to pass the address of the pointer, you need to change the function also like below example.

do_something(x, &ptr);

void do_something(int n, line_details** ptr)
{
    /* some code */
}
kadina
  • 5,042
  • 4
  • 42
  • 83
1

The function signature depends on what the function is supposed to do with the pointer. Here are some examples.

// Wants to change the pointer, so needs a pointer to the pointer.
void ld_alloc(line_details** ppl) {
    *ppl = malloc(sizeof(line_details));
    ...
}

// Wants to free the pointer, so the pointer would be sufficient.
// But here we have some safety mechanism and change the pointer to NULL,
// so we need a pointer to the pointer.
void ld_free(line_details** ppl) {
    ...
    free(*ppl);
    *ppl = NULL;
}

// No need to change the pointer, so just pass it.
// No need to change the line_details, so I made it const.
void ld_print(const line_details* pl) {
   ...
}

// This function might or might not need to change the pointer, so we must
// pass a pointer to the pointer.
// We can pass this pointer to the pointer to ld_alloc, but we can also pass
// the pointer itself to ld_print.
void ld_alloc_if_null_then_print(line_details** ppl) {
    if(*ppl == NULL) {
        ld_alloc(ppl);
    }
    ld_print(*ppl);
}

line_details* ptr = NULL;
ld_alloc(&ptr);
ld_print(ptr);
ld_free(&ptr);
ld_alloc_if_null_then_print(&ptr);
Werner Henze
  • 16,404
  • 12
  • 44
  • 69
  • In `ld_alloc`, `ppl` is already `**`, then why `*ppl = malloc(sizeof(line_details));`? – Losnikov Mar 02 '21 at 19:32
  • 1
    @Losnikov First `malloc(sizeof(line_details))` returns a `line_details*`, so the left side of the assignment must have the same type. Second you don't want to change `ppl` which would only be the local parameter variable inside `ld_alloc`. You want the side effect of changing the callers `ptr` (which is where `ppl` points to, so `*ppl`; remember: `ppl==&ptr` and `*ppl==ptr`). – Werner Henze Mar 02 '21 at 19:35
  • In `ld_alloc_if_null_then_print` you send `ptr` which is of type `line_details*` but `ld_alloc` accepts `line_details**`? Is it a mistake or I misunderstand something? – Losnikov Mar 02 '21 at 20:15
  • 1
    @Losnikov That was a typo, `ptr` should have been `ppl`. I fixed it. – Werner Henze Mar 02 '21 at 20:37