最近碰到的一个关于memcpy的奇葩问题

在使用memcpy函数时,遇到了编译错误提示Stack Overflow,这在C编程中非常罕见。错误源于/usr/include/x86_64-linux-gnu/bits/string3.h中的__builtin___memcpy_chk。尽管GCC版本为4.9.2,该问题在GCC 4.7.0中已被修复。为解决此问题,选择手动逐位复制数据,避免使用memcpy。


最近写代码,碰到一个奇葩问题,memcpy函数用起来,编译居然提示我stackoverflow,简直是对写C的码农的最大羞辱,WTF!

    UINT8 numBuffers = 0;
    UINT16 cpLength = 0;
//  TPM2B_DIGEST *bufferList;
    if(length%(MAX_DIGEST_BUFFER) != 0)
        numBuffers = length/(MAX_DIGEST_BUFFER) + 1;
    else
        numBuffers = length/(MAX_DIGEST_BUFFER);

    TPM2B_DIGEST *bufferList[numBuffers];
    for(UINT8 i = 0; i < numBuffers; i++)
    {
        (bufferList)[i] = (TPM2B_DIGEST *)calloc(1,sizeof(TPM2B_DIGEST));
        if(i < numBuffers-1)
        {
//          memcpy(bufferList[i]->t.buffer, &buffer[cpLength], MAX_DIGEST_BUFFER);
            for( UINT16 m = 0; m < MAX_DIGEST_BUFFER; m++)
            {
                bufferList[i]->t.buffer[m] = buffer[m + cpLength];
            }
            cpLength = i * MAX_DIGEST_BUFFER;
        }
        if(i == numBuffers-1 )
        {
            for(UINT16 j= 0; j < (length-cpLength); j++)
            {
                bufferList[i]->t.buffer[j] = buffer[cpLength + j];
            }
        }

    }

就是上面注释掉的
memcpy(bufferList[i]->t.buffer, &buffer[cpLength], MAX_DIGEST_BUFFER);

编译的时候,结果如下:

In file included from /usr/include/string.h:639:0,
                 from tpm2_sign.cpp:61:
In function ‘void* memcpy(void*, const void*, size_t)’,
    inlined from ‘int computeDataHash(BYTE*, UINT16, TPMI_ALG_HASH, TPM2B_DIGEST*)’ at tpm2_sign.cpp:669:73:
/usr/include/x86_64-linux-gnu/bits/string3.h:53:71: error: call to void* __builtin___memcpy_chk(void*, const void*, long unsigned int, long unsigned int) will always overflow destination buffer [-Werror]
   return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest));

自查半天,没觉得有什么错,网上查了半天,居然查到一个“GCC Bugzilla – Bug 37060”,翻到最后跟我说是“fixed in GCC 4.7.0”

链接在此   GCC Bugzilla -Bug 37060


我gcc -v 查自己的gcc版本, 妈蛋, 都4.9.2了啊,我勒个去。

只好折衷,选个别的办法解决问题,一位位的复制吧,不用memcpy了。


            for( UINT16 m = 0; m < MAX_DIGEST_BUFFER; m++)
            {
                bufferList[i]->t.buffer[m] = buffer[m + cpLength];
            }


尼玛,现在还没弄明白,到底是个神马情况,有时间好好扒一扒这家伙的老底,奶奶个腿儿的,先记下来!


<think>好的,我现在需要帮用户举一个使用memcpy的例子。首先,我得回想一下memcpy函数的基本用法。memcpy是C语言标准库中的一个函数,用于将一块内存的内容复制到另一块内存中。它的原型是void *memcpy(void *dest, const void *src, size_t n),其中dest是目标地址,src是源地址,n是要复制的字节数。 用户可能已经知道基本的用法,但需要具体的例子来理解实际应用场景。我应该选择一个简单但能体现memcpy作用的例子。比如复制数组或者结构体。结构体的复制可能更有代表性,因为结构体通常包含不同类型的数据,这样可以展示memcpy处理内存块的能力。 接下来,我需要考虑代码的结构。首先包含必要的头文件,比如stdio.h和string.h,因为memcpy在string.h中声明。然后定义一个结构体,比如学生信息,包含姓名、年龄和分数。接着在main函数中创建两个结构体实例,一个作为源,一个作为目标。使用memcpy将源结构体复制到目标结构体,最后打印结果来验证复制是否成功。 需要注意的是,结构体可能会有填充字节,但memcpy会逐字节复制,所以即使有填充,也能正确复制。不过用户可能不会关心这些细节,所以例子中不需要深入讨论填充问题,除非用户特别提到。 另外,要确保例子的代码正确,没有语法错误。比如结构体的定义,变量初始化,以及printf的正确使用。同时,注释应该清晰,解释每一步的作用,这样用户能更好地理解。 可能用户还想知道memcpy和其他复制方法的区别,比如直接赋值或者循环复制。这时候可以简单提到memcpy的高效性,尤其是在处理大块内存时,但例子中暂时不需要展开,保持例子的简洁。 最后,要检查代码是否实际运行过,确保输出符合预期。比如,复制后的结构体各个字段应该和源结构体一致,这样用户运行代码时能得到正确的结果,增强例子的可信度。 总结一下,步骤是:定义结构体,初始化源结构体,使用memcpy复制到目标结构体,打印结果。同时给出代码解释和输出说明,帮助用户理解整个过程。</think>--- ### **`memcpy` 基础介绍** `memcpy` 是C语言标准库函数,用于将**指定字节数**的数据从**源内存地址**复制到**目标内存地址**。其函数原型为: ```c void *memcpy(void *dest, const void *src, size_t n); ``` - **`dest`**:目标内存地址(需确保可写且足够大)。 - **`src`**:源内存地址(需确保可读)。 - **`n`**:要复制的字节数。 - **返回值**:返回目标地址 `dest`。 --- ### **示例场景:结构体复制** 假设需要快速复制一个包含学生信息的结构体,无需逐字段赋值: ```c #include <stdio.h> #include <string.h> // 包含 memcpy 的声明 // 定义结构体 typedef struct { char name[20]; int age; float score; } Student; int main() { Student stu1 = {"Alice", 18, 92.5}; // 源结构体 Student stu2; // 目标结构体 // 使用 memcpy 复制 stu1 到 stu2 memcpy(&stu2, &stu1, sizeof(Student)); // 验证复制结果 printf("姓名: %s\n年龄: %d\n分数: %.1f\n", stu2.name, stu2.age, stu2.score); return 0; } ``` --- ### **代码解析** 1. **结构体定义**: - `Student` 包含三个字段:字符数组 `name`、整型 `age`、浮点型 `score`。 2. **内存复制**: - `memcpy(&stu2, &stu1, sizeof(Student))`: - 从 `stu1` 的地址开始,复制 `sizeof(Student)` 字节数据到 `stu2`。 - `sizeof(Student)` 自动计算结构体占用的总字节数。 3. **输出结果**: ```plaintext 姓名: Alice 年龄: 18 分数: 92.5 ``` --- ### **关键点说明** | 行为 | 解释 | |------------------|----------------------------------------------------------------------| | **逐字节复制** | `memcpy` 不关心数据类型,直接按字节复制内存内容。 | | **高效性** | 比逐字段赋值更高效,适合大块内存或频繁复制场景。 | | **重叠问题** | 若源和目标内存有重叠,应使用 `memmove` 而非 `memcpy`(后者行为未定义)。 | --- ### **典型应用场景** 1. **数组复制**: ```c int src[5] = {1, 2, 3, 4, 5}; int dest[5]; memcpy(dest, src, sizeof(src)); // 复制整个数组 ``` 2. **深拷贝内存块**: ```c char buffer[100]; const char *data = "Example Data"; memcpy(buffer, data, strlen(data) + 1); // +1 包含字符串结束符 '\0' ``` 3. **网络数据传输**: ```c struct Packet packet; char raw_data[1024]; memcpy(&packet, raw_data, sizeof(packet)); // 将二进制数据解析为结构体 ``` --- ### **注意事项** 1. **内存越界**: - 确保目标内存空间足够大,否则可能覆盖其他数据或导致崩溃。 - 错误示例: ```c int a = 10; int b; memcpy(&b, &a, sizeof(int) * 2); // 试图复制 8 字节到仅 4 字节空间 ``` 2. **类型安全**: - `memcpy` 不检查数据类型,若复制不兼容的内存块可能导致逻辑错误。 - 错误示例: ```c float f = 3.14; int i; memcpy(&i, &f, sizeof(float)); // 将浮点数的二进制表示直接赋给整数 ``` --- ### **与 `strcpy` 的对比** | 函数 | 用途 | 终止条件 | 安全性 | |------------|----------------------------|-----------------------|----------------------| | `memcpy` | 复制任意内存块 | 按指定字节数复制 | 需手动控制长度 | | `strcpy` | 复制以 `\0` 结尾的字符串 | 遇到 `\0` 停止 | 易导致缓冲区溢出 | --- 通过这个例子,可以清晰理解 `memcpy` 的直接内存操作特性。在实际开发中,需特别注意内存边界和数据类型匹配问题
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值