Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

malloc and pointers to pointers

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?

like image 350
Idr Avatar asked Dec 26 '15 11:12

Idr


2 Answers

  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;
}
like image 100
alk Avatar answered Sep 29 '22 02:09

alk


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.

like image 32
loginn Avatar answered Sep 29 '22 03:09

loginn