C++ OSX+上的内存损坏/结构重新排序;llvm/libc++;

C++ OSX+上的内存损坏/结构重新排序;llvm/libc++;,c++,macos,llvm,memory-corruption,C++,Macos,Llvm,Memory Corruption,在最终得到一个跨平台项目进行编译之后,我在OSX上遇到了最奇怪的错误。程序以不同的方式崩溃(但有时可能存活下来以显示其ui)。通过使用XCode调试器,我看到多个地方的子对象值根据上下文而变化。这就是我的问题所在: class third { public: int some_data; void do_something() { } }; class second { public: third * thirdPtr; second()

在最终得到一个跨平台项目进行编译之后,我在OSX上遇到了最奇怪的错误。程序以不同的方式崩溃(但有时可能存活下来以显示其ui)。通过使用XCode调试器,我看到多个地方的子对象值根据上下文而变化。这就是我的问题所在:

class third
{
public:
    int some_data;
    void do_something()
    {
    }

};

class second
{
public:
    third * thirdPtr;

    second()
        : thirdPtr(nullptr)
    {
        thirdPtr = new third();
        printf("second = 0x%X, third = 0x%X", this, thirdPtr);
    }

};


class first
{
    second * secondInstance;
    first()
        : secondInstance(nullptr)
    {
        secondInstance = new second();
        printf("second = 0x%X, third = 0x%X", secondInstance, secondInstance->thirdPtr);
        // (maybe) crash
        secondInstance->thirdPtr->do_something();

    }

};
在逐步执行时,我在第三个指针上添加了一个观察点,这是一个示例输出:

Watchpoint 1 hit:
old value: 0x00000001
new value: 0x00000000

Watchpoint 1 hit:
old value: 0x00000000
new value: 0x04821c34
下面是节目的标准:

second = 0x4821C20, third = 0x4821C34
second = 0x4821C20, third = 0x3404821C
我从未见过这样的事。显然,结构更复杂,并且使用继承,但是在初始化时,没有其他任何东西可以访问或写入这些结构。我的源代码中有很多奇怪的小故障和问题,所以我认为这个小故障是多个地方的问题。请注意,源代码已经在其他平台上完美地运行了很长一段时间,甚至在外部库代码中也会出现故障(我在项目中使用juce)

起初我怀疑新操作符是假的,但它似乎和对象的组成有关。有趣的是,两个不同的三分之一似乎有很大的相似性

我在这里真的很茫然,我准备得出结论,llvm会在编译单元之间重新安排结构的布局(类/结构当然在不同的文件中)。。或者别的什么。我想我错了,但是有人经历过这样的事情吗

---------编辑-----------:

因此,我添加了以下代码: (第二个构造函数内):

(在第一个构造函数内):

它打印出

13
16

注:这是完整结构的输出(即不是此处给出的示例)。但是:结构的布局实际上是不同的,这取决于我所处的编译/翻译单元。这到底是怎么回事

---------编辑2--------:

因此,我决定检查对齐情况,这可能是关键问题:

来自第二个构造函数内部的标准输出:

    {
        thirdPtr = new third();
        printf("second = 0x%X, third = 0x%X", this, thirdPtr);
        printf("position = %d", offsetof(second, thirdPtr);
    }
    {
        secondInstance = new second();
        printf("second = 0x%X, third = 0x%X", secondInstance, secondInstance->thirdPtr);
        printf("position = %d", offsetof(second, thirdptr));

    }
second = 0x3649090, third = 0x36490A4
offset of third in second = 16
sizeof second = 232
align of third = 4, align of second = 4
second = 0x3649090, third = 0xA4036490
offset of second in third = 13
sizeof second = 226
align of third 1, align of second 1
来自第一个构造函数内部的标准输出:

    {
        thirdPtr = new third();
        printf("second = 0x%X, third = 0x%X", this, thirdPtr);
        printf("position = %d", offsetof(second, thirdPtr);
    }
    {
        secondInstance = new second();
        printf("second = 0x%X, third = 0x%X", secondInstance, secondInstance->thirdPtr);
        printf("position = %d", offsetof(second, thirdptr));

    }
second = 0x3649090, third = 0x36490A4
offset of third in second = 16
sizeof second = 232
align of third = 4, align of second = 4
second = 0x3649090, third = 0xA4036490
offset of second in third = 13
sizeof second = 226
align of third 1, align of second 1
因此,在翻译单位中,对齐似乎发生了变化。如何在整个项目中实施标准

编辑3:我通过对第二个类应用属性((打包))使它“运行”,如中所示,它不会立即崩溃。但这并没有让我感到安全,为什么我要这么做?是否有全局设置以翻译单位操纵此设置

------编辑4:解决方案----

包含的标头具有不匹配的#pragma pack(llvm显然支持)指令,如下所示:

    #ifdef __MSVC__
        #pragma pack(push, 1)
        #pragma warning(disable:4482)
    #else
        #pragma pack (push, 1)
    #endif

    ....

    #if defined(__MSVC__)
        #pragma pack(pop)
    #endif
    #ifdef __MSVC__
        #pragma pack(push, 1)
    #else
        #pragma pack (push, 1)
    #endif

    ....

    #if defined(__MSVC__)
        #pragma pack(pop)
    #endif

任何编译器都会更改打包堆栈,但只有在使用msvc++编译器的情况下才能恢复打包堆栈。这使得整个项目的打包工作只剩下1部分

在处理用户定义的结构打包对齐时,重置打包堆栈的重要性显而易见

VisualStudio编译器和clang的llvm前端都支持使用pragma指令的相同语法,可在此处研究:

在我的例子中,包含的标题有一个不匹配的#pragma pack指令,如下所示:

    #ifdef __MSVC__
        #pragma pack(push, 1)
        #pragma warning(disable:4482)
    #else
        #pragma pack (push, 1)
    #endif

    ....

    #if defined(__MSVC__)
        #pragma pack(pop)
    #endif
    #ifdef __MSVC__
        #pragma pack(push, 1)
    #else
        #pragma pack (push, 1)
    #endif

    ....

    #if defined(__MSVC__)
        #pragma pack(pop)
    #endif
任何编译器都会更改打包堆栈,但只有在使用msvc++编译器的情况下才能恢复打包堆栈。这使得包含此文件的翻译单元的打包对齐方式与未包含此文件的翻译单元的打包对齐方式不同,即使两个翻译单元看到的结构定义完全相同。为了完整起见,以下是(在我的例子中)更正的#pragma指令:

    #if defined(__MSVC__) || defined (__LLVM__)
        #pragma pack (push, 1)
    #endif

    #if defined(__MSVC__) || defined (__LLVM__)
        #pragma pack(pop)
    #endif  

您是否在64位操作系统上运行?如果是这样的话,
printf()
可能不可靠,因为您正在传递64位指针,但是
%X
需要32位值(我想-我不确定OS X是否使用LP64数据模型),它在64位操作系统上,但它是32位构建。调试器显示了正确的地址。“但是:结构的布局实际上是不同的,这取决于我所在的编译/翻译单元”-这听起来像是在编译一个翻译单元时提供了不同的打包选项。仔细看一下构建日志。我不确定我应该寻找什么,我在源代码中没有使用这样的东西(打包选项),两个源代码文件共享相同的编译器/项目设置,一次编译完成。此外,sizeof()显示了6个字节的差异,这取决于调用它的位置。Packing/alignment显然与示例不同,因此除非重新定义结构,否则Packing/alignment看起来像是被修改了。你尝试过检查<代码>对齐方式()/<代码>吗?你可能想考虑结构布局打包是否真的是必要的。我只在第一种情况下做它,因为它是需要的:它是不同语言之间的一些共享C结构的一部分。