Why int gets its value after being cast in C

I'm trying to use a struct to access the first member, but the values โ€‹โ€‹of the first member of the structure are getting changed / corrupted.

Here is the code:

typedef struct ObjectBase
{
    int integer1;
    int integer2;
}ObjectBase;

typedef struct ObjectExtended
{
    ObjectBase* baseObj;
    char* string;
}ObjectExtended;

int main(int argc,char** argv)
{
    void* voidObject = malloc(sizeof(ObjectExtended));
    ObjectExtended* objExtended = voidObject;
    objExtended->string = "TEST_OBJECT";

    objExtended->baseObj = malloc(sizeof(ObjectBase));
    objExtended->baseObj->integer1 = 10;
    objExtended->baseObj->integer2 = 11;

    printf("Extended Object:\n");
    printf("\tString: %s\n",objExtended->string);
    printf("\tInt1: %i\n",objExtended->baseObj->integer1);
    printf("\tInt2: %i\n",objExtended->baseObj->integer2);

    ObjectBase* objBase = voidObject;

    printf("Base Object:\n");
    printf("\tInt1: %i\n",objBase->integer1);
    printf("\tInt2: %i\n",objBase->integer2);

    free(objExtended->baseObj);
    free(objExtended);
    return 0;
}

      

Receives output:

Extended Object:
    String: TEST_OBJECT
    Int1: 10
    Int2: 11
Base Object:
    Int1: 166544
    Int2: 6

      

Does he get 166544 and 6 from?

+1


source to share


2 answers


You are using "virtual inheritance", so it ObjectExtended

does not contain ObjectBase

as its first member, but rather a pointer to it. When you put ObjectExtended *

in ObjectBase *

, you start to interpret the upper and lower order bytes of that pointer as integers ObjectBase

(assuming sizeof(int *) == 2 * sizeof(int)

).

To fix this problem, use "non-virtual inheritance", that is, declare ObjectExtended

as

typedef struct ObjectExtended
{
  ObjectBase baseObj;  /* not a pointer */
  char* string;
} ObjectExtended;

      

which would probably be a better design anyway, or change your casting logic:

#define UPCAST(EXTPTR) ((EXTPTR)->baseObj)

      

Unlike dynamic_cast

in C ++ (which you ultimately meant when you wrote your code), hovering a pointer in C simply instructs the compiler to re-compact any memory location the pointer points to as an object of a new type, if the first member of yours struct

is a base object , this is ok because the base object and the extended object start at the same address. But in your source code, you need more than reinterpreting the destination of the pointer. The base object address is not extended, but different. Namely, it is the one stored in the pointer baseObj

. You can see that they cannot have the same address that you created using two different calls malloc

.

To make things clearer, here is the memory layout of your objects:



+----------------+<--+  +                         +
| integer1       |   |  | sizeof (int)            | sizeof
+----------------+   |  +                         | (ObjectBase)
| integer2       |   |  | sizeof (int)            |
+----------------+   |  +                         +
                     |
+----------------+   |  +                         +
| baseObj        |---+  | sizeof (ObjectBase *)   | sizeof
|                |      |                         | (ObjectExtended)
+----------------+      +                         |
| string         |---+  | sizeof (char *)         |
|                |   |  |                         |
+----------------+   |  +                         +
                     V
                    somewhere else ...

      

The gap between the two objects is of course not scalable. The two calls malloc

can return any addresses in any order. *

Instead of ObjectExtended

being declared as above ("not virtual"), you would get the following layout:

+----------------+      +                  + +
| integer1       |      | sizeof (int)     | | sizeof
+----------------+      +                  | | (ObjectBase)
| integer2       |      | sizeof (int)     | |
+----------------+      +                  | +
| string         |---+  | sizeof (char *)  | sizeof
|                |   |  |                  | (ObjectExtended)
+----------------+   |  +                  +
                     V
                    somewhere else ...

      

In both images, the compiler could add extra padding bytes to the objects so that they are consistent in memory as required by the target architecture.

* And this is in contrast to a language like C ++ with built-in support for inheritance, where using virtual inheritance does not result in heap allocation for the sub-object. Instead, the C ++ compiler calculated the sum of both memory requirements and then either reserved enough stack space to accommodate the entire object, or allocated the required amount of memory in free storage (if the object was created using an operator new

). You could do the same in C using malloc(sizeof(ObjectExtended) + sizeof(ObjectBase))

and then compute the memory layout yourself. But you have to be more careful not to break the alignment rules.

+4


source


ObjectBase* objBase = voidObject;

      

Here you are assigning a void pointer, which is a pointer struct ObjectExtended

. So with the above purpose, you are trying to assign one structure to another, which has a completely different memory structure and is incompatible. Hence, you get a strange result.

Since you are using a pointer void

, the compiler cannot help you here, as pointers are void

compatible with any type of object pointer.

Either avoid using a pointer void

as that is pretty much the root cause of this confusion I think (although using a pointer correctly is void

fine).

 ObjectExtended * objExtended = malloc(sizeof(ObjectExtended));
 //ObjectExtended* objExtended = voidObject; This is not needed anymore.

      

and when you later want to access the member, use the correct member for the assignment:

ObjectBase* objBase = objExtended->baseObj;

      



This usage would give you a warning, for example:

warning: initialization from incompatible pointer type

      


OR

If you want to stick with a void pointer then use the correct object type by doing this:

ObjectBase* objBase = ((struct ObjectExtended*) voidObject)->baseObj;

      

+1


source







All Articles