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

GCC环境下strlen与strcpy函数行为异常问题咨询及原因解析

为什么两个C程序的strcpy输出会不一样?

这是一个典型的数组越界导致未定义行为的问题,而且根源出在早已被废弃的gets函数上——咱们一步一步拆解为什么两个程序会有完全不同的输出:


核心问题:未定义行为的根源

你的两个程序里,char p[5]只分配了5个字符的空间,但你输入的shadab qureshi长度远大于5。而gets函数的致命缺陷就是完全不检查输入长度:它会把你输入的所有字符一股脑写到p的起始地址,超过p的空间后,就会覆盖栈上的其他内存(比如p1、变量k甚至main函数的返回地址)。

这种行为在C语言里被称为未定义行为——意思是编译器和系统可以随便怎么处理,出现任何结果都不奇怪,两个程序的差异正是未定义行为的不同表现。


第一个程序(未使用strlen)的行为解析

当你输入长字符串后:

  • p的5个空间被填满('s','h','a','d','a'),剩下的字符('b',' ','q','u','r','e','s','h','i','\0')会继续往栈上的下一块内存写(因为变量声明顺序是k→p→p1,栈一般是向下增长的,所以p之后的内存就是p1)。
  • 执行strcpy(p1,p)时,p里没有字符串终止符'\0'(因为输入太长,'\0'被写到了p1的后面),所以strcpy会从p开始一直读内存,直到找到那个在p1后面的'\0'——这就导致p1被复制了完整的输入字符串。
  • 但之后执行puts(p)时,栈上的内存已经被严重覆盖,甚至可能在strcpy写p1的过程中,反过来修改了p所在的内存(栈内存是连续的,越界写的影响是连锁的),所以最终p的起始地址被改成了'e','s','h','i'加'\0',输出eshi。

第二个程序(使用strlen)的行为解析

加入k = strlen(p)后,栈内存的状态被改变:

  • strlen的工作逻辑是从p的起始地址开始遍历,直到找到'\0'才返回长度。但p里没有'\0',所以它会一直读栈上的内存,直到碰到某个恰好是'\0'的垃圾值(或者之前越界写的某个位置)。
  • 找到长度后,这个值会被赋值给k——而k的内存位置就在p的上方(栈布局的原因),这相当于修改了栈上靠近p的一块内存。
  • 当执行strcpy(p1,p)时,此时p后面的内存(被k修改过的位置)恰好出现了'\0',所以strcpy只复制了p的前5个字符('s','h','a','d','a')加'\0',因此p1和p的输出都是shada。

关键总结 & 正确修复方案

  • 永远不要再用gets:这个函数在C11标准里已经被移除,因为它是缓冲区溢出漏洞的重灾区。改用fgets,比如:
    fgets(p, sizeof(p), stdin);
    
    它会自动限制输入长度,避免越界。
  • 字符串操作必须保证终止符和空间足够:定义字符数组时,一定要预留足够的空间存放字符串内容+'\0';永远不要让输入长度超过数组的大小。
  • 未定义行为不可预测:这两个程序的不同输出只是未定义行为的两种表现,换个编译器、换个系统甚至换个输入时机,结果都可能不一样——不要试图去“预测”未定义行为的结果,而是要从根源避免它。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 11:57:32