首页 > 解决方案 > 为什么调用 memcpy 到 bool 值后 memcpy to int 不起作用

问题描述

当我偶然发现一个奇怪的结果时,我正在玩 memcpy,在 bool memcpy 之后在同一个内存指针上调用的 memcpy 给出了意想不到的结果。

我创建了一个简单的测试结构,它有一堆不同的类型变量。我将结构转换为 unsigned char 指针,然后使用 memcpy 将该指针中的数据复制到单独的变量中。我尝试绕过 memcpy 的偏移量并在 bool 之前移动 int memcpy (更改了测试结构的布局,以便 int 也将在 bool 之前)。令人惊讶的是,这种转变解决了这个问题。

// Simple struct containing 3 floats
struct vector
{
    float x;
    float y;
    float z;
};

// My test struct
struct test2
{
    float a;
    vector b;
    bool c;
    int d;
};

int main()
{
    // I create my structure on the heap here and assign values
    test2* test2ptr = new test2();
    test2ptr->a = 50;
    test2ptr->b.x = 100;
    test2ptr->b.y = 101;
    test2ptr->b.z = 102;
    test2ptr->c = true;
    test2ptr->d = 5;

    // Then turn the struct into an array of single bytes
    unsigned char* data = (unsigned char*)test2ptr;
    // Variable for keeping track of the offset
    unsigned int offset = 0;

    // Variables that I want the memory copied into they
    float a;
    vector b;
    bool c;
    int d;

    // I copy the memory here in the same order as it is defined in the struct
    std::memcpy(&a, data, sizeof(float));
    // Add the copied data size in bytes to the offset
    offset += sizeof(float);

    std::memcpy(&b, data + offset, sizeof(vector));
    offset += sizeof(vector);

    std::memcpy(&c, data + offset, sizeof(bool));
    offset += sizeof(bool);

    // It all works until here the results are the same as the ones I assigned
    // however the int value becomes 83886080 instead of 5
    // moving this above the bool memcpy (and moving the variable in the struct too) fixes the problem
    std::memcpy(&d, data + offset, sizeof(int));
    offset += sizeof(int);

    return 0;
}

所以我预计 d 的值为 5 但它变为 83886080 我认为这只是随机未初始化的内存。

标签: c++

解决方案


您忽略了结构中数据的填充。

看一下下面的简化示例:

struct X
{
    bool b;
    int i;
}; 

int main()
{
    X x;
    std::cout << "Address of b " << (void*)(&x.b) << std::endl;
    std::cout << "Address of i " << (void*)(&x.i) << std::endl;
}

这在我的 PC 上产生:

b的地址 0x7ffce023f548

我的地址 0x7ffce023f54c

如您所见,bool结构中的值在此处占用 4 个字节,即使它的内容使用较少。编译器必须向结构添加填充字节,以使 cpu 可以直接访问数据。如果您的数据按照代码中编写的方式线性排列,编译器必须在所有访问时生成汇编指令,以便稍后对齐数据,这会大大降低您的程序速度。

您可以通过添加pragma pack或与您的编译器类似的东西来强制编译器执行此操作。所有的pragma东西都是编译器特定的!

对于您的程序,如果memcpy您要访问的元素之前的数据而不是数据元素的大小,则必须使用地址,因为这会忽略填充字节。

如果我pragma pack(1)在我的程序之前添加一个,输出是:

b的地址 0x7ffd16c79cfb

我的地址 0x7ffd16c79cfc

如您所见,bool 和 int 之间不再有填充字节。但是稍后将访问 i 的代码将非常大而且很慢!所以完全避免使用#pragma pack


推荐阅读