为什么一个int获取其值用C铸造后改变 [英] Why is an int getting its value changed after casting in C

查看:117
本文介绍了为什么一个int获取其值用C铸造后改变的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想投一个结构来访问它的第一个成员,但该结构的第一个成员的值得到改变/搞砸。

I'm trying to cast a struct to access it's first member, but the values of the first member of the struct are getting changed/messed up.

继承人的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;
}

继承人的输出:

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

哪来它获得166544&放大器; 6来自哪里?

Wheres it getting the 166544 & 6 from?

推荐答案

您使用的是虚继承,所以 ObjectExtended 并的的包含一个 ObjectBase 作为它的第一个成员,而是一个指针之一。当你施放一个 ObjectExtended * ObjectBase * ,启动跨preting的高位和低位字节该指针的整数 ObjectBase (假设的sizeof(INT *)== 2 * sizeof的(INT) )。

You are using "virtual inheritance" so the ObjectExtended does not contain an ObjectBase as its first member but rather a pointer to one. As you cast an ObjectExtended * to an ObjectBase *, you start interpreting the high and low order bytes of that pointer as the integers of the ObjectBase (assuming sizeof(int *) == 2 * sizeof(int)).

要解决这个问题,要么使用非虚继承,即申报 ObjectExtended

To fix that problem, either use "non-virtual inheritance", ie declare ObjectExtended as

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

这将可能被摆在首位的更好的设计,无论如何,或者改变你的施法逻辑:

which would have probably been the better design in the first place anyway, or change your casting logic:

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

不像的dynamic_cast 在C ++中(你最终不得不在心里的编写code),铸造用C的指针只是指示编译器重新间$ p $角任何存储器位置的指针指向作为新类型的对象。如果你的结构为基本对象的第一个成员,这是好的,因为基础对象和扩展对象都开始在同一地址。但在你原来的code,你需要比指针的目的地的reinter pretation多。基本对象的地址的的是,延长之一,但不同的。也就是说,它是存储在 baseObj 指针之一。你可以看到,他们不能有相同的地址,你让他们使用两种不同的的malloc 通话创建。

Unlike a dynamic_cast in C++ (which you eventually had in mind while writing your code), casting a pointer in C simply instructs the compiler to re-interpret whatever memory location the pointer points to as an object of the new type. If the first member of your struct is the base object, this is fine, because the base object and the extended object both start at the same address. But in your original code, you'll need more than a reinterpretation of the pointer's destination. The address of the base object is not that of the extended one but different. Namely, it is the one stored in the baseObj pointer. You can see that they cannot have the same address as you have created them using two different malloc calls.

为了让事情更清楚,这里是你的对象的内存布局:

To make things more clear, 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 ...

两个对象之间的间隙,当然不是按比例的。 的malloc两个电话可以按任何顺序返回任何地址。 *

The gap between the two objects is of course not to scale. Two calls of malloc may return any addresses in any order.*

要是 ObjectExtended ,而不是被宣布为上述(非虚拟),你已经得到了以下布局:

Had ObjectExtended instead been declared as above ("non-virtual"), you'd have gotten the following layout:

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

在两个图片中,编译器可能已经增加了额外的填充字节的对象,使它们在存储器所要求的目标结构一致。

In both pictures, the compiler might have added additional padding bytes to the objects to make them align in memory as required by the target architecture.

<分> * 这是相对于像C ++用语言内置的继承,其中使用虚拟继承不会导致该子对象的动态内存分配的支持。相反,C ++编译器会计算双方的内存需求的总和,然后要么保留足够的堆栈空间来容纳整个对象或分配的自由存储所需的内存量(如果对象是使用运营商创建的)。你可以通过使用已经做在C相同的的malloc(sizeof的(ObjectExtended)+的sizeof(ObjectBase)),然后弄清楚内存布局自己。但是,你必须更加小心不要违反排列规则。

* And this is in contrast to a language like C++ with built-in support for inheritance where using virtual inheritance does not cause a dynamic memory allocation for the sub-object. Instead, a C++ compiler would have computed the sum of both memory requirements and then either reserve enough stack-space to accommodate for the entire object or allocate the required amount of memory on the free store (if the object were created using operator new). You could have done the same in C by using malloc(sizeof(ObjectExtended) + sizeof(ObjectBase)) and then figure out the memory layout yourself. But you'd have to be more careful not to violate alignment rules.

这篇关于为什么一个int获取其值用C铸造后改变的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆