简体   繁体   中英

Casting memory into a struct pointer

When I do something like:

struct my_struct {
    uint32_t n;
    double   d;
    uint64_t *ptr;
    size_t   val;
};

struct my_struct a;

and in a function:

void a_func(struct my_struct *a) {
    a = (struct my_struct *) [a memory location];
}

I do not get correct values in a;

but when I do something like:

void a_func(struct my_struct *a) {
    *a = *(struct my_struct *) [same memory location];
}

I get correct values in the struct;

Any reasonable explanation for this?

Lets look at three different cases:

  1. Change pointer locally

     void foo(S *a) { a = p; } S* b; foo(b); 

    a is a pointer and this function changes the pointer a . It does not change the object a is pointing to. It also does not change b or the object b is pointing to.

  2. Change object pointed to

      void foo(S *a) { *a = *p; } S* b = ...; foo(b); 

    *a = *p performs a deep copy. It copies the object pointed to by p over the object pointed to by a . As b points to the same object as a , b will also see these changes.

  3. Get pointer for usage outside the function

      void foo(S **a) { *a = p; } S* b; foo(&b); 

    Now the function foo accepts a pointer to a pointer. By writing *a = p we change the pointer pointed to by a to p . This can be used to retrieve the pointer p as b will be the same as p after the call to foo .

I assume you call that function and then try to use the a parameter after the function returned, eg

a_func(a);
printf("a->n: %u", a->n);

In both cases, you pass the pointer a by value. Changing the pointer itself in a_func() will not be reflected outside of a_func() . Put another way, a inside of a_func() is a copy of a outside, so changes to the pointer will not reflected outside after returning.

Changing the memory a points to will be visible outside, though.

In the first case (without * ), you assign a itself in a_func() . As just explained, the new value of a will be lost as soon as a_func() returns.

In the second case (with * ), you copy the memory from [a memory location] the memory pointed to by a . This means, that the memory a points to has to be valid: either it has to be on the stack, or dynamically allocated on the heap. Passing an uninitialized struct my_struct * pointer will lead to crashes sooner or later.

Once you return, you can access the data copied via the a pointer you passed to a_func() .

Example for correctly using the copy version (with * ) with a local variable a :

struct my_struct a;         // Allocate a my_struct object on the stack.
a_func(&a);                 // Copy data from [some memory location] into a.
printf("a.n: %u", a.n);     // Access and use the newly copied data in a.

Another correct version with a allocated on the heap:

// Allocate a my_struct object on the heap and make a point to that memory.
struct my_struct *a = malloc(sizeof(my_struct)); 
a_func(a);                  // Copy data from [some memory location] into a.
printf("a->n: %u", a->n);   // Access and use the newly copied data in a.
free(a);                    // Take care to free the allocated memory when finished!

A broken example:

struct my_struct *a;        // An uninitialized pointer!
a_func(a);                  // The memory location a points to is overwritten - BUG!
printf("a->n: %u", a->n);   // May still work but you corrupted your memory with
                            // the previous function call. This will lead to crashes!

It's the same as trying to change an integer from 3 to 5 inside a function and then failing. Check the following example:

#include <stdio.h>

void func( int a ) {
    a = 5;
}

int main ( ) {
    int x = 3;
    func( x );
    printf( "%d", x );
    // prints 3 not 5

    return 0;
}

This is because, when you pass x variable into func here, you pass its value, that is 3 ; func creates a variable named a , assigns it with the passed value 3 , assigns it again with the value 5 . No change has been made to x there, so x still is just 3 .

If you were to pass the address of x as a value to some other function that takes an address as an argument, then access the contents of that address and change it, then you'd be able to remotely change the x , as in the following example:

#include <stdio.h>

void anotherfunc( int * a ) {  // a is an address-holding variable
    *a = 5;  // *a is the content of that address and we are changing it to 5
}

int main ( ) {
    int x = 3;
    func( &x );  // passing the address of the variable x
    printf( "%d", x );
    // prints 5 now

    return 0;
}

Same story for your case, only with one further step of referencing/dereferencing. If you want to make the first version work out, make changes similar to the following:

void a_func(struct my_struct ** a) {  // added an asterisk
    *a = (struct my_struct *) [a memory location];
}

// ...

int main( ) {
    // ...
    struct my_struct * x;
    a_func( &x );

    // ...
    return 0;
}

Here, the a_func takes the address of an address-holding variable (pointer) as an argument, and stores that inside a newly created variable called a , which is a variable that holds address to an address to a struct my_struct . Then it accesses the contents of the address a holds, assigns that with a memory location, and so on...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM