【问题标题】:c++ memory in array of class objects类对象数组中的c ++内存
【发布时间】:2012-08-15 01:42:38
【问题描述】:

我有这样的课:

class Object {
public: 
    unsigned char data[8];
    // other variables
    // functions etc...
 };

问题是 - 对象成员是否都存储在内存中相对于对象的相同位置?所以如果我有一个数组:对象数组[3],给定一个字符指针 char* data_ptr = array[0].data, 会 data_ptr + (sizeof(Object)) 然后总是指向array[1].data?

(我已经阅读了一些关于如何在类和结构的数据成员之间进行填充的 Q/As - 但我认为它们没有回答我的问题。)

提前致谢, 本

【问题讨论】:

    标签: c++ class memory layout member


    【解决方案1】:

    sizeof Object 已包含 Object 类的所有内部填充。包括结尾处的任何填充。数组不允许任何额外的填充。因此data_ptr + sizeof Object 的地址确实是array[1].data

    然而我不确定这是否真的被允许。也就是说,编译器可能被允许假定您永远不会将大于 8 的值(成员数组 data 的大小)添加到 array[0].data,因此它可能会应用失败的优化如果你违反规则。也就是说,您的代码实际上可能表现出未定义的行为(这是“允许编译器在这种情况下做任何事情”的标准术语)。

    然而因为您使用的是指向 char 的指针,对此有更宽松的规则(您可以用 char* 做很多事情,而用一般类型做不到),它无论如何,它可能是实际定义的行为。

    【讨论】:

    • 虽然char* 可以很好地处理通用数据,但unsigned char*uint8_t 不是原始字节访问的首选方法吗?
    • 确实,对于访问原始字节,unsigned char+uint8_t 是更好的选择(因为在非 2 的补码机器上,零有两种表示形式)。然而,更宽松的规则也适用于char。在这种特殊情况下,不会发生原始字节访问;指针最终指向的数组又是一个char的数组。
    • 我不明白你的第二段(为什么是 8 段?)(对不起 - 我是 C++ 新手!)。目前它似乎对我有用(使用它将 unsigned char 值从一个数组复制到另一个数组),但我显然想确保将来不会出现问题!
    • @user1483596: 8 因为您已将成员 data 定义为 char data[8]。也就是说,将 8 添加到 array[0].data 会为您提供该成员数组的过去结束指针,这当然是允许的。添加任何更大的东西意味着你离开了数组,这可能是未定义的行为(这意味着在这种情况下允许编译器做它想做的任何事情,因此如果你这样做优化可能会出错)。
    • 根据经验,您可以将“未定义的行为”翻译为“不要这样做”。
    【解决方案2】:

    如果您的问题的目的是了解事物在内存中的情况,那么它是可以接受的。

    但如果你想真正做到这一点:你想做的实际上是对你所有同事的犯罪

    在 C++ 中通过集合的正确方法不是使用数组,而是使用 std::vector 或另一个 std 集合如果确实需要。我们不应该再使用 C 算术,而是通过迭代器访问向量集合的项目。这就是 C++ 标准库的原因:-)

    【讨论】:

    • 我将研究迭代器 - 以前没有使用过它们(我是 C++ 新手)。我之所以选择这种指针方法是因为我需要将数据元素从数千个对象复制到另一个数组,然后我使用 opengl 绘制。
    • 您还可以在 C 数组上实例化 std::vector:我将在我的答案下方发布代码,仅供您参考,以向您展示如何操作。我建议您学习如何使用 std 库。慢慢地/按照你的节奏没关系,欢迎使用 c++ ;-)
    • 太糟糕了,我说得太早了......虽然很容易从 std::vector (指向它的第一个元素,并考虑它的大小)获得一个标准的 c 数组,在相反,我还没有找到一种方法让一个新的 std::vector 指向一个已经存在的数组(据我所知,这是 opengl 提供的):如果不复制数组的内容就不可能这样做,这可能是像图像一样,对于大数组来说效率低下。
    • 没问题 - 我会调查自己。我已经有一个 Array 类作为纯数组的包装器,我会看看我可以多么容易地将它更改为包装 std::vector。
    【解决方案3】:

    我认为答案是“也许”,这意味着你不应该赌这个。最好的方法是在 IDE 调试器上查找内存地址。当您引入编译器可以优化的成员和方法时,您会发现这很容易被抛弃。例如,任何不访问可能静态成员的常量或方法。例如:

    void Object::doSomething() {std::cout << "something\n" << std::endl;}
    

    我相信这实际上在静态分配中得到了优化,因为我最近了解到这个 ((Object)NULL).doSomething(); 实际上在没有 SEGFAULT 的情况下也可以工作,直到你为 Object 引入一个成员变量。

    【讨论】:

    • 有趣 - 两个与我的问题中的数据数组相同的类,一个具有打印功能,一个没有相同的大小。但是指针算法似乎仍然按预期工作......
    【解决方案4】:

    在 C++ 中,有许多因素决定了一个类的对象的大小。这些因素是: - 所有非静态数据成员的大小 - 数据成员的顺序 - 字节对齐或字节填充 - 直接基类的大小 - 虚函数的存在(使用虚函数的动态多态性)。 - 正在使用的编译器 - 继承方式(虚拟继承)

    在这里查看:http://www.cprogramming.com/tutorial/size_of_class_object.html

    【讨论】:

    • 虽然对象的大小确实可以通过非常复杂的规则来确定,但在这种特殊情况下这些规则是无关紧要的,因为数组包含完整的对象(您不能定义不完整对象的数组),因此sizeof Object 已经包含任何填充、隐藏成员或其他内容。
    【解决方案5】:

    是的,相对于对象基址的布局将是不变的。这是 ABI 的要求。对象和数组的任何间距或填充也由 ABI 指定。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-17
      • 1970-01-01
      • 1970-01-01
      • 2011-12-10
      • 2021-12-14
      • 1970-01-01
      • 2016-01-23
      相关资源
      最近更新 更多