Why is *ptr.member wrong and (*ptr).member right? - c

I know that if I have a pointer in C programming language, and I have to point to a struct, I do:
struct mystruct S;
struct mystruct *ptr;
ptr=&S;
ptr->member=5;
I understood this, but I do not understand the reason why, if I do:
ptr=&S;
(*ptr).member=5;
*ptr should point to the first address of the struct, but why doesn't it work if I do this:
*ptr.member=5;

Operators have precedence: the member access operator . has the highest precedence, while dereference * has the next highest precedence. So *p.member attempts to find the member member of a pointer to mystruct and then dereference it. Since a *mystruct does not have a member called member, this is an error.

Because the access to member operator (the dot) has precedence over the dereferenciation (*).
So, your expression is equivalent to:
*(p.member) = 5
And p.member is not a pointer.

Unary * has a lower precedence than the . and -> member selection operators, so *p.member is parsed as *(p.member); in other words, you're attempting to dereference the result of p.member. This has two problems:
p is a pointer type, so the compiler will complain that you're trying to use the . operator on something that isn't a struct or union type;
member isn't a pointer type, so you can't apply the unary * to it.
(*p).member is equivalent to p->member; both dereference p before accessing member.

If you have a structure
struct A
{
int x;
};
and an object of the structure type
A a;
then to access its data member x you have to write
a.x = 5;
Or you can write
( &a )->x = 5;
Now if you have a pointer of type struct A * that initialized by the address of the object a
struct A *pa = &a;
then expression *pa gives the object a itself. So you may write
( *pa ).x = 5;
So now compare. On the one hand
a.x = 5; and ( *pa ).x = 5;
and on the other hand
( &a )->x = 5; and pa->x = 5;

You have a struct and a pointer to the struct:
struct mystruct S;
struct mystruct *ptr;
Now you store in ptr the address of that struct
ptr = &S;
When you need to access one member of that struct through a pointer, you have TWO alternative syntaxes
(*ptr).member = 5;
and
ptr->member = 5;
The two syntaxes are equivalent and do exactly the same action.
Is only a matter of personal taste.
In your question you have an error, because you wrote:
(*p).member = 5;
Instead of
(*ptr).member = 5;
Hence the error
One last tip:
(*ptr).member = 5;
and
*ptr.member = 5;
Are different things
(*ptr).member = 5; means that you get the address pointed by ptr and then store 5 in the field named member
*ptr.member = 5; means that you get the address pointed by ptr.member and then store 5 in the memory pointed by that address.

Related

What is the difference between ptr->thing and *ptr->thing in C?

My understanding is that the -> operator is shorthand for dereferencing a pointer to a struct, and accessing the value of one struct member.
struct point {
int x;
int y;
};
struct point my_point = { 3, 7 };
struct point *p = &my_point; /* p is a pointer to my_point */
(*p).x = 8; /* set the first member of the struct */
p->x = 8; /* equivalent method to set the first member of the struct */
So the last 2 lines of the example above are equivalent. But I've encountered some code similar to this:
*p->x = 8
Using both the asterisk and arrow together. What does this do? Would this try to "double dereference" the pointer and assign to memory address 8, or something else? Maybe undefined behavior, or just a compiler error?
*p->x is equivalent to *(p->x) - you are dereferencing the result of p->x, which implies the x member itself has pointer type. Given a line like
*p->x = 8;
that implies x has type int *:
struct foo {
...
int *x;
...
};
Note that x must be assigned a valid pointer value before you can assign to *x. You can allocate memory dynamically:
struct foo *p = malloc( sizeof *p ); // dynamically allocate space for 1 instance of struct foo
if ( !p )
// handle allocation failure and exit here.
p->x = malloc( sizeof *p->x ); // dynamically allocate space for 1 int.
if ( !p->x )
// handle allocation failure and exit here.
Or you can set x to point to an existing int object:
int a;
...
p->x = &a;
*p->x = 8;
For a structure
struct tagp
{
int *x=someaddress;
}p0;
struct tagp *p=&p0;
*p->x accesses the address stored in the pointer x inside the structure. It is same as *((*p).x) and *(p0.x) which accesses the memory at Someaddress.
Check this link

Why is it dangerous?

I don't understand some topic from book Extreme C on page 300. It's about "multiple inheritance".
typedef struct { ... } a_t;
typedef struct { ... } b_t;
typedef struct {
a_t a;
b_t b;
...
} c_t;
c_t c_obj;
a_t* a_ptr = (a_ptr*)&c_obj;
b_t* b_ptr = (b_ptr*)&c_obj; //it's the problem
c_t* c_ptr = &c_obj;
Why we should do something like that??
c_t c_obj;
a_t* a_ptr = (a_ptr*)&c_obj;
b_t* b_ptr = (b_ptr*)(&c_obj + sizeof(a_t)); //?Is the address a_ptr the same as address c_obj?
c_t* c_ptr = &c_obj;
Thank you very much for all your help.
Why we should do...?
We should not do this!
First, sizeof a might be wrong to advance the address. If b_t as larger alignment requirements than a_t this will not yield the correct offset.
Second, the expression is wrong:
(&c_obj + sizeof(a_t))
This will take address of c_obj which has type c_t*. Then it will add a multiple of sizeof (c_t) which again points to object of type c_t but with an address that is illegal.
Third: Your casts are all wrong. You need to use name of a type, not a variable.
If you want to get address of b, there is a macro in C lib available offsetof:
offsetof(c_t,b)
evaluates to the offset in bytes of member b inside type c_t.
Then you can apply this to your address:
b_ptr=(b_t*) ((unsigned char*)&c_obj) + offsetof(c_t,b));
The first cast to unsigned char is required to use byte arithmetics.
Of course, there is a much simpler way to do this:
b_ptr=&c_obj.b;
Maybe the point of the book was to show that you cannot just use address of a struct and cast it to a pointer to another struct but you have to take care about the location of the members inside that struct. That is correct.
But the dirty details were a bit off.
Keep it simple:
c_t c_obj;
a_t* a_ptr = &c_obj.a;
b_t* b_ptr = &c_obj.b;
c_t* c_ptr = &c_obj;
The problem with:
b_t* b_ptr = (b_ptr*)&c_obj;//it's the problem
is that b_ptr ends up pointing to c_obj.a.
The problems with:
b_t* b_ptr = (b_ptr*)(&c_obj + sizeof(a_t));//?Is the address a_ptr the same as address c_obj?
are:
You are trying to adjust the pointer by sizeof(a_t) bytes, but pointer arithmetic is scaled by the size of the dereferenced type of the pointer. In this case, the pointer type is c_t* (from the expression &c_obj), and the dereferenced type is c_t, so the pointer is actually being adjusted by sizeof(c_t) * sizeof(a_t) bytes.
There may be padding after some of the members of c_t. In particular, there may be padding between the a and b members, so the b member may not be at the offset that you think it is. The offset of b from the start of c_t in bytes can be determined using the expression offsetof(c_t, b).
The address of an object of a structure type is equal to the address of the first member of the structure type.
So using your example
typedef struct {
a_t a;
b_t b;
...
} c_t;
c_t c_obj;
a_t* a_ptr = (a_ptr*)&c_obj;
then indeed the address of the data member a is equal to the address of the object c_obj.
However for the data member b this relation is broken because the data member b is not the first data member of the structure c_t.
As for this statement
b_t* b_ptr = (b_ptr*)(&c_obj + sizeof(a_t));
then it is entirely wrong. For starters in this sub-expression &c_obj + sizeof(a_t) there is used the pointer arithmetic and the value of the expression &c_obj is incremented by the value sizeof( c_t ) * sizeof( a_t ) .
It seems you mean
b_t* b_ptr = (b_ptr*)(( char * )&c_obj + sizeof(a_t));
However in any case the expression in the right side in general will not yield the address of the data member b due to a possible alignment,
Consider the following demonstrative program.
#include <stdio.h>
struct A
{
int x;
};
struct B
{
double y;
};
struct C
{
struct A a;
struct B b;
};
int main(void)
{
struct C c = { { 1 }, { 2.2 } };
printf( "&c.a = %p\n( char * )( &c.c ) + sizeof( struct A ) = %p\n&c.b = %p\n",
( void * )&c.a, ( void * ) ( ( char * )&c.a + sizeof( struct A ) ), ( void * )&c.b );
return 0;
}
Its output might look like
&c.a = 0x7ffe5e0697c0
( char * )( &c.c ) + sizeof( struct A ) = 0x7ffe5e0697c4
&c.b = 0x7ffe5e0697c8
As you see the data member a was appended with bytes to align the next data member b to double.

Operator -> in malloc function

Watch here:
struct mystruct{
int a;
int b;
};
int main(void){
struct mystruct* ptr;
ptr = malloc( 10*sizeof( struct mystruct ) );
In this way I have allocated an array of struct.
If you try to print, for example ptr[4], you will notice a pointer.
When I have a pointer and I have access to a member, I have to use the -> operator right?
But if I do:
ptr[4]->a
It doesn't work! I must do
ptr[4].a
to make it work... why? I have a pointer!
ptr[4] = (ptr+4) right?
Also in normal arrays this happen:
struct mystruct array[10];
array[4].a
but array is a pointer!
array[4] is not the same as array + 4. It's the same as *(array + 4).
array + 4 is certainly a pointer, but array[4] is the value pointed to by the pointer (which, of course, might also be a pointer if array were an array of some pointer type.)
Since x->y is essentially the same as (*x).y, you could rewrite array[4].a as (array + 4)->a, but you'd have a hard time getting that past most code reviewers.
when you use square brackets on a pointer, it's similar to dereferencing the point. Therefore, it requests the p[].a syntax, and not the p[]->a
(p + 4)->a is essentially the same as p[4].a
If ptr has type struct mystruct *, then ptr[i] has type struct mystruct; IOW, ptr[i] is not a pointer value. Thus, you need to use the . component selection operator instead of ->.

Casting struct * to int * to be able to write into first field

I've recently found this page:
Making PyObject_HEAD conform to standard C
and I'm curious about this paragraph:
Standard C has one specific exception to its aliasing rules precisely designed to support the case of Python: a value of a struct type may also be accessed through a pointer to the first field. E.g. if a struct starts with an int , the struct * may also be cast to an int * , allowing to write int values into the first field.
So I wrote this code to check with my compilers:
struct with_int {
int a;
char b;
};
int main(void)
{
struct with_int *i = malloc(sizeof(struct with_int));
i->a = 5;
((int *)&i)->a = 8;
}
but I'm getting error: request for member 'a' in something not a struct or union.
Did I get the above paragraph right? If no, what am I doing wrong?
Also, if someone knows where C standard is referring to this rule, please point it out here. Thanks.
Your interpretation1 is correct, but the code isn't.
The pointer i already points to the object, and thus to the first element, so you only need to cast it to the correct type:
int* n = ( int* )i;
then you simply dereference it:
*n = 345;
Or in one step:
*( int* )i = 345;
1 (Quoted from: ISO:IEC 9899:201X 6.7.2.1 Structure and union specifiers 15)
Within a structure object, the non-bit-field members and the units in which bit-fields
reside have addresses that increase in the order in which they are declared. A pointer to a
structure object, suitably converted, points to its initial member (or if that member is a
bit-field, then to the unit in which it resides), and vice versa. There may be unnamed
padding within a structure object, but not at its beginning.
You have a few issues, but this works for me:
#include <malloc.h>
#include <stdio.h>
struct with_int {
int a;
char b;
};
int main(void)
{
struct with_int *i = (struct with_int *)malloc(sizeof(struct with_int));
i->a = 5;
*(int *)i = 8;
printf("%d\n", i->a);
}
Output is:
8
Like other answers have pointed out, I think you meant:
// Interpret (struct with_int *) as (int *), then
// dereference it to assign the value 8.
*((int *) i) = 8;
and not:
((int *) &i)->a = 8;
However, none of the answers explain specifically why that error makes sense.
Let me explain what ((int *) &i)->a means:
i is a variable that holds an address to a (struct with_int). &i is the address on main() function's stack space. This means &i is an address, that contains an address to a (struct with_int). In other words, &i is a pointer to a pointer to (struct with_int). Then the cast (int *) of this would tell the compiler to interpret this stack address as an int pointer, that is, address of an int. Finally, with that ->a, you are asking the compiler to fetch the struct member a from this int pointer and then assign the value 8 to it. It doesn't make sense to fetch a struct member from an int pointer. Hence, you get error: request for member 'a' in something not a struct or union.
Hope this helps.

lvalue required as unary '&' operand while finding offset of a structure variable inside an another structure

I am trying to access elements i, f and ch of the following structure.
struct a{
struct b{
int i;
float f;
char ch;
}x;
struct c{
int j;
float g;
char ch;
}y;
};
This has to be done by passing the address of structure c to a function and calculating its offset to access structure b
struct a z;
fun(&z.y);
The function is as follows:
fun(struct c *p){
int offset;
struct b *address;
offset = (char *) &((struct c*)(&((struct a*)0)->y)->j) - (char *)((struct a*)0);
address = (struct b*)((char *)&(p->j)-offset);
address->i = 400;
address->f = 3.14;
address->ch = 'c';
}
Despite trying a lot, it always gives an error of lvalue required as unary '&' operand
I know the meaning of the error, but isn't able to get it done here.
Thanks in advance.
& can only be used on variables (including array elements or struct members) - more formally called lvalues. Essentially, you can use it on anything that could be on the left side of an = (this is how lvalues got their name). You can't do, for example, &(1 + 1). You also can't do &(&some_variable) for the same reason: &some_variable is not an lvalue.
It's easy to see these are not lvalues, as you couldn't do 1 + 1 = something; or &some_variable = something;.
You have &((struct c*)(&((struct a*)0)->y)->j). Or simplified, &(&(something)). This is not valid. I don't know what the function is trying to do, but perhaps you've used two &s instead of one by a simple mistake.

Resources