Search code examples
cpointersstructpaddingsizeof

How to access data of another variable by pointing to a slack-byte in a structure?


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

struct someStruct {
    int ivar;
    char cvar;
    float fvar;
};

main(argc,argv)
const char** argv;
{
    struct someStruct someObj;
    someObj.ivar = 1;
    someObj.fvar = 2.3;
    someObj.cvar = 'r';
    printf("%u\n",&someObj.fvar);
    printf("%u\n",(&someObj.cvar + 4));
    printf("%f\n",*((&someObj.cvar) + 4));
    printf("%f\n",someObj.fvar);
}

This is a program I wrote which tries to access the address of fvar by adding 4 to the address of char. I know the concept of slack-byte. I tried to access that memory, and lo! it printed the address correctly.

printf("%u\n",&someObj.fvar);
printf("%u\n",(&someObj.cvar + 4));

Both print the same address.

Then I tried to access fvar in the following way:

printf("%f\n",*((&someObj.cvar) + 4));
printf("%f\n",someObj.fvar);

and

0.000000
2.300000

were my results.

Then I realised that char* and float* are interpreted differently.

So, I tried all kinds of typecasting from char* to float* to float and also from char to float* to float and that too at all possible points like for e.g., typecasting the returned address to float,using 4.0 instead of 4 (which I realised wasn't supposed to work... and it didn't),etc.

It somehow just doesn't print 2.300000 and keeps printing 0.000000

Where am I missing the concept?

Note that I have a 64-bit MinGW and adds slack-byte (I know some don't) which I've already verified by:

printf("%u",sizeof(someObj.ivar));
printf("%u",sizeof(someObj.fvar));
printf("%u",sizeof(someObj.cvar));
printf("%u",sizeof(someObj));

which yields 4, 4, 1 and 12 ....(sizeof(char) + 3) respectively.

P.S. I know this is a horrible idea, but this is how I usually learn concepts XD


Solution

  • Assuming that the following is true:

     &someObj.cvar + 4 == &someObj.fvar
    

    you can cast the pointer value to a proper type:

    printf("%f\n", *(float*)((&someObj.cvar) + 4));
    

    (&someObj.cvar) is a pointer to char, so *(&someObj.cvar) is a char. The %f printf specifier expects a double or a float, passing a char is invalid. Note: float when passed in variadic functions as one of the arguments in ellipsis parameter is implicitly converted to double, see ex. cppreference implicit conversions. You have to pass a double or a float to %f, not a char.

    Notes:

    main(argc,argv)
    const char** argv;
    {
    

    Don't use implicit int declaration and old, deprecated, obsolete style of function declaration. Use the normal style:

    int main(int argc, char **argv) {
    
    • printf("%u\n",&someObj.fvar); is undefined behavior. The %u expects unsigned char, not float *. You can print a void* pointer using %p specifier: printf("%p\n", (void*)&someObj.fvar); or cast it to unsinged int: printf("%u\n", (unsigned int)(uintptr_t)(void*)&someObj.fvar);

    • C has an macro offsetof declared in stddef.h to access the number of "slack-bytes" (I like the name "padding bytes" better). You can: printf("%f", *(float*)((char*)&someObj + offsetof(struct someStruct, fvar)));