简体   繁体   中英

How to access second member of struct via pointer?

I have seen the first address of struct is simultaneously the first address of first member of that struct. Now what I would like to understand is, why I need always double pointer to move around in the struct:

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

struct foo
{
    char *s;
    char *q;
};

int main()
{
    struct foo *p = malloc(sizeof(struct foo));
    char ar[] = "abcd\n";
    char ar2[] = "efgh\n";
    *(char**)p = ar;
    *(char**)((char**)p+1) = ar2; //here pointer arithmetic (char**)p+1

    printf("%s\n",p->q);
}

the question is, why do I need char** instead of simple char* ? What I saw in assembler is in case of simple char* , the arithmetic would behave like normal char . That is -> the expression of (char*)p+1 would move the address p just by one byte (instead of 8 as address are 8 bytes long). But yet the type char* is address, so I don't get why the arithmetic behave like the dereference type instead (plain char -> one byte).

So the only solution for me was to add another indirection char** , where the pointer-arithmetic magically takes 8 as size. So why in structs is needed such bizarre conversion?

You are doing funny things. You should just do:

struct foo *p = malloc(sizeof(struct foo));
char ar[] = "abcd\n";
char ar2[] = "efgh\n";
p->s = ar;
p->q = ar2; 

First of all, what you are doing is slightly bizarre. It's also unsafe, since there may be padding between struct members and your address calculation may be off (that's likely not true in this particular case, but it's something to keep in mind).

As to why you need multiple pointers...

The type of p is struct foo * - it's already a pointer type. Each of the members s and q have type char * . To access the s or q members, you need to dereference p :

(*p).s = ar;    // char * == char *
(*p).q = ar2;   // char * == char *

So if you're trying to access the first character pointed to by s through p , you're trying to access a character through a pointer ( s ) through another pointer ( p ). p does not store the address of the first character of s , it stores the address of the thing that stores the address of the first character of s . Hence the need to cast p to char ** instead of char * .

And at this point I must emphasize DON'T DO THIS. You can't safely iterate through struct members using a pointer.

The -> operator was introduced to make accessing struct members through a pointer a little less eye-stabby:

p->s = ar;  // equivalent to (*p).s = ar
p->q = ar2; // equivalent to (*p).q = ar2

As the address of an object of a structure type is equal to the address of its first member then you could write for example

( void * )&p->s == ( void * )p

Here is a demonstrative program

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

struct foo
{
    char *s;
    char *q;
};

int main(void) 
{
    struct foo *p = malloc(sizeof(struct foo));
    
    printf( "( void * )p == ( void * )&p->s is %s\n",
           ( void * )p == ( void * )&p->s ? "true" : "false" );
           
    return 0;
}

Its output is

true

So the value of the pointer p is equal to the address of the data member s .

In other words a pointer to the data member s is equal to the pointer p .

As the type of the data member s is char * then pointer to s has the type char ** .

To assign the pointed object you need to cast the pointer p of the type struct foo * to the type char ** . To access the pointed object that is the data member s you have to dereference the pointer of the type char ** .

As a result you have

*(char**)p = ar;

Now the data member s (that is the pointer of the type char * ) is assigned with the address of the first element of the array ar .

In the second expression the left most casting is redundant

*(char**)((char**)p+1) = ar2;
 ^^^^^^^^

because the expression (char**)p+1 is already has the type char ** . So you could just write

*((char**)p+1) = ar2;

why do I need char** instead of simple char*

With pointer usage, the the left side of the assignment, code needs the address of the object.

*address_of_the_object = object

As the object is a char * , the type on the left side, the address of the object , needs to be type char ** .


How to access second member of struct via pointer?

Better to instead use the sensible:

p->q = ar2;

... then the convoluted:

//          |-- address of p->q as a char * ----|
*((char **) ((char *)p + offsetof(struct foo, q))) = ar2;
//|------------ address of p->q as a char ** ---|

OP's *(char**)((char**)p+1) = ar2; is amiss as it does the wrong pointer math and assumes no padding.


Convoluted approach details.

To portable find the offset within a struct , use offsetof(struct foo, q) . It returns the byte offset and will accounts for potential padding. Add that to a char * version of the struct address to do the proper pointer addition to form the address of p->q . That sum is a char * , Convert to the type of the address of the object. Lastly de-reference it on the LHS as part of the assignment.

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