Reputation: 617
I recently saw a code in C which had my wondering.
A simple form of the code is as below
typedef unsigned int uint32_t;
typedef struct
{
uint32_t easy;
uint32_t hard;
}mode;
typedef struct
{
uint32_t status;
}type;
int main()
{
type t2 = { 0 };
type* t1 = NULL;
t1 = &t2;
t1->status = 99;
((mode*)&(t1))->easy = 1;
printf("%d", ((mode*)&(t1))->easy);
scanf_s("%d", &((mode*)&(t1))->hard);
printf("%d", ((mode*)&(t1))->hard);
printf("%d", t1->status);
}
But when I execute the above, I get an error in the last printf statement as 'Access violation reading location 0x00000001'.
Any help would be highly appreciated.
Upvotes: 0
Views: 9283
Reputation: 9203
At first everything looks fine. Even I was stuck at why is the throwing access violation. BTW the violation is at the last printf.
Then I realized the mistake. The t1 is already a pointer. They didn't have to do &(t1) everywhere. That changed the contents of t1 itself not the struct it was pointing to. Secondly, there is not enough memory allocated to *t1 to be used as a mode So you can make t2 to be of type mode and cast it before assigning it's address to t1. Else you end up editing the value to t1 when changing hard since t1 is just on the stack after t2
So the easy fix would be
typedef unsigned int uint32_t;
typedef struct
{
uint32_t easy;
uint32_t hard;
}mode;
typedef struct
{
uint32_t status;
}type;
int main()
{
mode t2 = { 0 };
type* t1 = NULL;
t1 = (type*) &t2;
t1->status = 99;
((mode*)(t1))->easy = 1;
printf("%d", ((mode*)(t1))->easy);
scanf_s("%d", &((mode*)(t1))->hard);
printf("%d", ((mode*)(t1))->hard);
printf("%d", t1->status);
}
And everything works as expected. No access violation.
Upvotes: 1
Reputation: 726539
This line stores an int
in the space allocated for a pointer:
((mode*)&(t1))->easy = 1;
Since t1
is a pointer, and your code takes a pointer to it with an ampersand, the value is not written to the status
field of t2
; it's written directly into the space of the pointer itself. This would correct the problem, writing 1
into the status
field, which is also an alias for easy
:
((mode*)t1)->easy = 1;
With this problem out of the way, the next problem happens on the scanf_s
line. This line has undefined behavior, even if you remove the ampersand:
scanf_s("%d", &((mode*)&(t1))->hard); // <<== Undefined behavior
scanf_s("%d", &((mode*)(t1))->hard); // <<== Also undefined behavior
The original line tries to write to stack memory past the place allocated to the t1
pointer, causing stack corruption when returning from scanf_s
.
The fixed line tries to write to the location of hard
which is beyond the end of the type
struct.
Note that it is OK to cast type*
to mode*
and access easy
on it: when two struct
s have identical initial members, you can freely cast pointers to one struct
to pointer type of the other struct
.
Upvotes: 4