malloc and pointers to pointers
Asked Answered
I

2

6

I'm trying to understand when I need to use malloc when using multiple levels of pointers. For example,

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

int main() {
    typedef struct {
        char first[10];
        char last[10];
    } Person;

    Person *p;

    p = malloc(sizeof(Person));
    strcpy(p->first, "John");
    strcpy(p->last, "Doe");

    printf("First: %s Last:%s\n", p->first, p->last);

    return 0;
}

In this first version I'm using Person *p and I only use malloc to allocation space for type Person. In the 2nd version, I'll change Person *p to Person **p

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

int main() {
    typedef struct {
        char first[10];
        char last[10];
    } Person;

    Person **p;

    *p = malloc(sizeof(Person));
    strcpy((*p)->first, "John");
    strcpy((*p)->last, "Doe");

    printf("First: %s Last:%s\n", (*p)->first, (*p)->last);

    return 0;
}

I'm still using only one malloc even though there is now another pointer.

In this third version, I'll use Person ***p

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

int main() {
    typedef struct {
        char first[10];
        char last[10];
    } Person;

    Person ***p;

    *p = malloc(sizeof(void));
    **p = malloc(sizeof(Person));
    strcpy((**p)->first, "John");
    strcpy((**p)->last, "Doe");

    printf("First: %s Last:%s\n", (**p)->first, (**p)->last);

    return 0;
}

My questions:

1) Why do I need to malloc space for **p in the 3rd version, but I don't need to malloc space for *p? They are both pointers to pointers?

2) Also, why don't I need to malloc space for p in either the 2nd or 3rd version?

3) In the third version, what is the right size to malloc for *p? On my 64 bit Mac the sizeof(void) is 1, and the sizeof(void*) is 8, and both seem to work but what is the right one?

Involute answered 26/12, 2015 at 11:5 Comment(9)
You do need to allocate p on v2. Your code is wrong.Habakkuk
v2 compiles and runs as expected for me.Involute
Recompile with warnings enabled and you will find a few problems with your code.Pastorale
You don't understand pointers. Do yourself a favor and learn.Habakkuk
the right one is void*. void* is a pointer which means its size is enough to contain every memory address in your 64 bit computer. It could be smaller if your computer was using a 32bit system. Also Amit is right, your second version is wrong and you need to allocate memory for the first pointer (p). If you compile with warning flags (-Wall -Wextra) you'll get this : warning: ‘p’ is used uninitialized in this function [-Wuninitialized] *p = malloc(sizeof(Person));Mahala
I turned on the warnings and see what you are saying. So for v3, is the right thing to do p = malloc(sizeof(void*)); *p = malloc(sizeof(void*)); **p = malloc(sizeof(Person)); or is there a better way to do it?Involute
@Involute That's correct. Although it would be even better to use malloc(sizeof(Person*)); instead of malloc(sizeof(void*));since you already know the type you're going to use. It doesn't change anything computer wise but makes the code clearer.Mahala
@ldr trial and error is not a good way to learn C, you can't tell whether your code is correct, or if it is broken but happening to produce the correct output this timeAdulterate
*p = malloc(sizeof(void)); should fail to compile, you meant sizeof (Person*) or more simply sizeof **pAdulterate
M
7
  1. Dereferencing a pointer (*p) which had not been initialised provokes undefined behaviour in any case.

  2. When allocating space to a pointer you mostly ever want to allocate to it memory with the size of what is it pointing to by typically using the sizeof operator. This latter case is the one and only exception to 1. that allows coding *p.

So the 3rd example could look like this

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

typedef struct {
        char first[10];
        char last[10];
} Person;

int main(void) {
    Person ***p;

    p = malloc(sizeof *p); 
    *p = malloc(sizeof **p);
    **p = malloc(sizeof ***p);
    strcpy((**p)->first, "John");
    strcpy((**p)->last, "Doe");

    printf("First: %s Last:%s\n", (**p)->first, (**p)->last);

    free(**p);
    free(*p);
    free(p);

    return 0;
}
Mannequin answered 26/12, 2015 at 15:0 Comment(1)
Really great demonstration of pointer-to-pointer-to-p... Thanks!Upstate
M
5

I'm just going to put this here to summarize :

1) You do need to allocate space for *p. If you run your second program with valgrind, you'll see an error for alloc size (1 instead of 8); *p is a pointer to pointer, but **p isn't, it's a pointer to a structure.

2) You do need to allocate space in both cases and if you turn on the warnings (which you should never turn off anyway), you'll get this warning :

warning: ‘p’ is used uninitialized in this function [-Wuninitialized] *p = malloc(sizeof(Person));

3) The right one is void*. void* is a pointer which means its size is enough to contain every memory address in your 64 bit computer. It could be smaller if your computer was using a 32bit system. Although it would be even better to use

malloc(sizeof(Person*)); instead of malloc(sizeof(void*));

Since you already know the type you're going to use. It doesn't change anything computer wise but makes the code clearer.

Mahala answered 26/12, 2015 at 11:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.