Why is it dangerous? - c

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.

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

How to set value of struct indirectly in c?

I would like to know, how to access first member of struct through pointer. I have tried this:
#include <stdio.h>
#include <stdlib.h>
struct foo
{
int a;
char *str;
};
int main()
{
struct foo *p = malloc(sizeof(struct foo));
int val = 10;
*(int**)p = &val; //set the value of the first member of struct foo
printf("%i\n",p->a);
}
but that print some garbage. How can I set it in similar manner?
What you're doing there is setting the value of p->a to the address of val (possibly truncated to 32 bits, depending on the implementation).
To access the field via a pointer, you have to take the field's address and then assign to the dereferenced pointer.
int *pa = &p->a;
*pa = val;
Of course you can also just write p->a = val. Note that p->a is just an abbreviation for (*p).a.
The assignment should be:
*(int*)p = val;
You want to assign to an int member, so the pointer has to be a pointer to int, not pointer to pointer to int. And the value being assigned must be int; &val is a pointer to int.

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

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.

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.

Dereferencing a pointer to a struct to access its first member

For specific reasons i want to access only the first member of a struct by dereferencing the pointer to the struct.
I would like to know if is this legal or can it cause UB under some conditions; and what would be a correct solution, if this one has any problems.
Thank you.
#include <stdio.h>
#include <stdlib.h>
typedef struct test_s
{
void * data ;
struct test_s * next ;
} test_t ;
int main( void )
{
test_t * t = calloc( 1 , sizeof( test_t ) ) ;
int n = 123;
t->data = &n ; //int is used only for an address, this could be anything, an object for example
void ** v = ( void* )t ;
printf("Address of n: %p\nAddress of *t: %p\n\n" , &n , *v ) ; //dereference the pointer to struct to access its first member
return 0;
}
Yes, this is legal. From C99, 6.7.2.1.13:
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.
Yes, this is 100% legal: C standard specifies that the pointer to a struct must always equal to the pointer to the initial member of that struct.

Resources