Semantics
Semantics

Reputation: 164

pointer to pointer for struct

I have the following code

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

typedef struct {
    int age;
} data;

int storage (data **str) {
    *str = malloc(4 * sizeof(**str));
    (*str)[0].age = 12;
    return 0;
}

int main() {
    data *variable = NULL;
    storage(&variable);
    return 0;
}

I took it from a website source. I think I have a misunderstanding about a basic pointer to pointer concept because here in this code, we have a pointer to a struct, variable, and we are passing this to storage function, which expects pointer to pointer of struct type. After memory was malloced, I don't understand this assignment

(*str)[0].age = 12

It was assigned as if, str was of (*)[] type. I dont understand how this assignment works, like str is now a pointer to an array of structs?

Upvotes: 4

Views: 178

Answers (4)

AndersK
AndersK

Reputation: 36082

It can be illustrated like this

main:
 data* variable = NULL;   //variable is a pointer
 storage(&variable)       //the address of the pointer is &variable 

the storage(data**) allows the function to take the address of the pointer variable

this allows storage to change what variable points to

In storage, the following statement changes what variable points to by dereferencing (since we did pass the address of variable):

*str = malloc(4 * sizeof(**str) )

The malloc allocates a memory block containing four structs (which each has the size sizeof(struct data) bytes)

A struct is just a convenient way to access a part of memory, the struct describes the layout of the memory. The statement

(*str)[0].age = 12;

is the equivalent of

data* d = *str; 
d[0].age = 12;

or you can write it as a ptr with offset:

data* d = *str;
*(d + 0).age = 12;

edit: a clarification about malloc

malloc returns a block of memory allocated in bytes, the return type of malloc is void* so per definition it has no type and can be assigned to a pointer of arbitrary type:

T* ptr = malloc(n * sizeof(T));

After the assignment to ptr, the memory is treated as one or more elements of type T by using the pointer T*

Upvotes: 2

user3185968
user3185968

Reputation:

First, a note about C syntax for dereferencing pointers:

a[b] is equivalent to *(a + b), is equivalent to *(b + a), is equivalent to b[a].

Now, in

int main() {
    data *variable = NULL;
    storage(&variable);
    return 0;
}

variable is of type "pointer to data", therefore its address &variable is of type "pointer to pointer to data". This is passed to int storage(data **str), and is the correct type for the argument str.

int storage (data **str) {
    *str = malloc(4 * sizeof(**str));
    (*str)[0].age = 12;
    return 0;
}

Here, str is dereferenced, yielding an lvalue of type data * designating the same object as main()s variable. Since it is an lvalue, it can be assigned to.

malloc() allocates memory without declared type, large enough (and sufficiently aligned) to contain four contiguous objects of type data. It returns a pointer to the beginning of the allocation.

(*str)[0] is now an lvalue designating an object of type data, and by accessing the memory malloc() allocated through this expression, the effective type of the memory becomes data. (*str)[0].age = 12; assigns the value 12 to the age-member of this object, leaving the other members of the struct (and the rest of the allocated memory) uninitialized.

Upvotes: 2

Harry
Harry

Reputation: 11638

This piece of code might help illustrate what's happening, the really interesting line is

assert(sizeof(**str2) == sizeof(data));

Your numbers may vary form mine but first lets create a struct with a rather dull but hard to fake size for testing purposes.

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

typedef struct {
  uint8_t  age;
  uint8_t  here_as_illustartion_only[1728];
} data;

int main() {
    data    str; 
    data *  str1 = &str;  
    data ** str2 = &str1;  
    printf("sizeof(str)    =%*zu\n", 5, sizeof(str));
    printf("sizeof(str1)   =%*zu\n", 5, sizeof(str1));
    printf("sizeof(str2)   =%*zu\n", 5, sizeof(str2));
    printf("sizeof(*str2)  =%*zu\n", 5, sizeof(*str2));
    printf("sizeof(**str2) =%*zu\n", 5, sizeof(**str2));

    assert(sizeof(**str2) == sizeof(data));
    return 0;
}

On my machine this prints the following

sizeof(str)    = 1729
sizeof(str1)   =    8
sizeof(str2)   =    8
sizeof(*str2)  =    8
sizeof(**str2) = 1729

Note the size of the pointer to pointer ie sizeof(**) is the dull number we're looking for.

Upvotes: 0

nalzok
nalzok

Reputation: 16107

Well, I think your code is simply identical to:

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

typedef struct
{
    int age;
} data;

int main()
{
    data *variable = NULL;
    variable = malloc(4 * sizeof(*variable));
    *(variable + 0).age = 12;
    return 0;
}

So variable is malloced with a block of memory, which is large enough to hold 4 datas(from variable[0] to variable[3]). That's all.

Upvotes: 0

Related Questions