You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

动态分配字符数组场景下strcpy函数工作异常原因排查

问题分析:两种C代码内存越界行为差异的原因

首先看第一段代码及其运行表现:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
void cpy(char *str) { 
    //char buffer[16]; 
    char *buffer = malloc(16 * sizeof(char)); 
    strcpy(buffer, str); 
    for (int i = 0; i < 300; i++) { 
        printf("buffer[%i]: %c \n", i, buffer[i]); 
    } 
} 
int main() { 
    //char large_str[256]; 
    char *large_str = malloc(256 * sizeof(char)); 
    for (int i = 0; i < 256; i++) { 
        large_str[i] = 'A'; 
    } 
    //large_str[255]= '\0'; 
    cpy(large_str); 
    return 0; 
} 

在Cygwin环境编译运行上述代码后,buffer[0]至buffer[299]大多为空,仅部分索引区间(如buffer[252]至buffer[264])存在乱码,buffer[0]至buffer[23]为'A'。

再看修改后的代码及运行结果:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
char buffer[16]; 
void cpy(char *buffer, char *str) { 
    //char *buffer = malloc(16 * sizeof(char)); 
    strcpy(buffer, str); 
    for (int i = 0; i < 300; i++) { 
        printf("buffer[%i]: %c \n", i, buffer[i]); 
    } 
} 
int main() { 
    //char large_str[256]; 
    char *large_str = malloc(256 * sizeof(char)); 
    for (int i = 0; i < 256; i++) { 
        large_str[i] = 'A'; 
    } 
    //large_str[255]= '\0'; 
    cpy(buffer, large_str); 
    return 0; 
} 

运行结果完全正常,buffer[0]至buffer[255]均为'A',其余索引为空。


核心原因:内存越界的未定义行为 + 不同内存区域的布局差异

两种代码都存在严重的内存越界问题——strcpy会一直写入直到遇到\0,但large_str没有设置终止符,所以会写入256个'A',远超过buffer的16字节容量。之所以表现不同,是因为buffer所在的内存区域完全不一样:

1. 第一种代码:堆内存越界的不可预测性

  • buffer是通过malloc分配的堆内存。堆内存由C标准库的分配器管理,为了维护内存块的分配状态,分配器会在每个堆块的前后添加元数据(比如块大小、空闲标记),而且通常会有对齐要求,实际分配的内存可能比你请求的16字节稍大(比如你看到的前23个'A',说明分配器给了至少24字节的块)。
  • 当你越界写入时,会破坏堆的管理元数据,甚至覆盖其他堆内存区域的内容。后续访问buffer[i]超过分配块的部分时,你其实在读取堆里的空闲区域、已被其他分配的内存,或者被破坏的元数据,这些内容完全不确定,所以会出现乱码、空值等随机表现。

2. 第二种代码:静态存储区越界的“看似正常”

  • buffer是全局数组,存储在静态存储区。这个区域的内存是程序启动时就分配好的,默认初始化为0,而且通常是一块连续的大内存块,在你的程序里暂时没有其他全局变量占用后面的空间。
  • 越界写入时,你只是在静态存储区里连续写入256个'A',后面的内存还是初始的0值,所以看起来“正常”。但这依然是未定义行为——如果后续添加其他全局变量,你会覆盖它们的值,导致程序出现莫名其妙的bug,而且不同编译器、不同系统下的静态存储区布局可能完全不同,表现也会不一样。

关键提醒

永远不要依赖未定义行为的表现!正确的做法是:

  • 确保目标缓冲区足够容纳要拷贝的内容(包括终止符\0)
  • 使用带长度限制的字符串函数,比如strncpy(注意手动添加终止符),或者更安全的strlcpy(部分系统支持)
  • 如果使用动态分配,记得根据源字符串的长度来分配足够的内存

内容的提问来源于stack exchange,提问作者Josh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:05:08