Addresses in structures

93 Views Asked by At

The following is an abstract version of a problem I am currently having.

#include <stdio.h>

int main()
{
    typedef struct {
        char * bar
    } struct_t;
    
    struct_t foo = {};
    foo.bar = "test";
    struct_t * p_foo = &foo;
    
    char * p = p_foo->bar;
    char ** a = &p_foo;
    char ** b = &p_foo->bar;
    
    printf("%s %s",*a,*b);

    return 0;
}

I expect the code to print test twice, which it doesn't. Why is that happening?

******************************* EDIT *******************************

The abstraction I WANTED to post is the following, it's basically what you guys pointed out to be wrong in my first attempt - thanks for that.

#include <stdio.h>

int main()
{
    typedef struct {
        char * bar;
    } struct_t;
    
    struct_t foo = { .bar = "test" };
    
    // works
    struct_t * p_foo = &foo;
    char * p_bar = &p_foo->bar[0];
    char ** a = &p_bar;
    
    // does not work
    char ** b = &p_foo->bar;
    
    printf("%s %s",*a,*b);

    return 0;
}

However, this version does actually print test twice, meaning that the abstraction does not reproduce my problem, so I have to try once again:

#include <stdio.h>

void callback(void ** ppContext)
{
    char * pString = (char *) *(ppContext);
    printf("%s",pString);
}

int main()
{
    typedef struct {
        char buffer[5];
    } struct_t;
    
    struct_t ctrl = { .buffer = "test" };
    struct_t * pCtrl = &ctrl;
    
    // works
    char * pString = &pCtrl->buffer[0];
    callback((void **) &pString);
    
    // does not work
    callback((void **) &pCtrl->buffer);

    return 0;
}

This is the actual code, I just deleted everything non-related. The section named //works prints test as expected. The section named //does not work does not. (I commented out one of them respectively to test it, an fflush inbetween them would make testing them simultaneously possible.)

I expect &pCtrl->buffer[0] to be the address of the first char in the buffer which is stored in pString. Since the callback is called with the address of pString, the (char *) *(ppContext) in the callback makes it the actuall string again, i.e., a pointer to the first char in the buffer.

However, I would expect &pCtrl->buffer to be the address of the string as well, if pCtrl->buffer is the string itself, i.e., the address of the first char in the buffer. But apparantly that is not the case and I don't understand why.

3

There are 3 best solutions below

0
KamilCuk On

Why is that happening?

a = &p_foo is a pointer to p_foo. p_foo is a pointer and points to foo. By doing *a you are trying to print the value of the pointer p_foo, i.e. the address of foo itself, as a string.

If you really want to do that, you might dereference a to get p_foo and then dereference p_foo to get the address of foo which is equal to the address of foo.bar which holds the address of the string to be printed:

char ***a = &p_foo;
printf("%s", **a);

But really, your code breaks language constraints and is invalid. You should use correct types and properly access structure members:

struct_t **a = &p_foo;    
printf("%s", (*a)->bar);
0
Vlad from Moscow On

For starters at least there is a typo in the structure declaration

typedef struct {
    char * bar
    ^^^^^^^^^^
} struct_t;

you need to place a semicolon after the data member declaration

typedef struct {
    char * bar;
} struct_t;

The initialization in this declaration

struct_t foo = {};

is invalid before the C23 Standard. You may not use empty braces if your compiler does not compile the program according to the C23 Standard. You could write for example

struct_t foo = { NULL };

or

struct_t foo = { .bar = NULL };

or simply

struct_t foo = { .bar = "test" };

For this declaration

char ** a = &p_foo;

the compiler should issue a message because there is no implicit conversion between the pointer types char ** and struct_t **.

As for your question then dereferencing the pointer a in the call of printf

printf("%s %s",*a,*b);

you get the pointer p_foo that points to the object of the type struct_t * declared like.

struct_t * p_foo = &foo;

The pointer does not contain the address of the first character of the string literal "test". Instead it contains the address of the object foo.

On the other hand the pointer p

char * p = p_foo->bar;

was assigned by the value of the pointer p_foo->bar that contains the address of the string literal. So using the expression *b in the call of printf yields the expected result opposite to using the the pointer expression *a.

Just compare how the pointers that is with which values they were initialized

foo.bar = "test";
struct_t * p_foo = &foo;

char * p = p_foo->bar;

In fact the last declaration

char * p = p_foo->bar;

looks like

char * p = "test";

of course provided that matching string literals are stored by the compiler as one string literal.

You can check the values of addresses produced by the pointer expressions *a and *b the following way

 printf("%p %p\n", ( void * )*a,( void * )*b);

To get the expected result you need to write

char **a = ( char ** )p_foo;

instead of

char ** a = &p_foo;

That is now the pointer a points to the object foo of the structure. Dereferencing the pointer you get the data member (pointer) bar address of which coincides with the address of the object foo that contains the data member bar.

0
Abhishek Ghosh On

The problem occurs with the b variable declaration: char ** b = &p_foo->bar;. Here, you are assigning the address of p_foo->bar, which is a char*, to a char** variable (b).

The correct type for b should be char*, not char**. This is because p_foo->bar is already of type char*. Assigning its address to a char** will lead to an incorrect level of indirection.

Please refer to the below code:

typedef struct {
    char * bar;
} struct_t;

struct_t foo = {};
foo.bar = "test";
struct_t * p_foo = &foo;

char * p = p_foo->bar;
char ** a = &p_foo;
char * b = p_foo->bar;

printf("%s %s", *a, b);