gvk
gvk

Reputation: 617

Type casting of structures in C

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

Answers (2)

Ajay Brahmakshatriya
Ajay Brahmakshatriya

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

Sergey Kalinichenko
Sergey Kalinichenko

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 structs have identical initial members, you can freely cast pointers to one struct to pointer type of the other struct.

Upvotes: 4

Related Questions