Day 2:C语言中的字符串结尾与缓冲区溢出

1. 原理与细节讲解

C语言中的字符串是以'\0'(null字符,ASCII为0)结尾的字符数组。操作字符串时,很多标准库函数(如strcpystrlenstrcat等)依赖这个结尾标志来判断字符串的边界。

常见问题在于:

  • 没有正确以'\0'结尾,导致函数继续读取/写入后续未初始化或属于其他变量的内存。
  • 拷贝或拼接时未考虑目标数组的大小,容易导致缓冲区溢出(Buffer Overflow)。

缓冲区溢出是C语言里最严重的漏洞之一,能导致崩溃、数据泄露,甚至攻击者执行任意代码(如著名的“栈溢出攻击”)。


2. 典型陷阱/缺陷

  • 字符串未以'\0'结尾

    char buf[5] = {'h', 'e', 'l', 'l', 'o'};  // 没有'\0'
    printf("%s\n", buf);  // 未定义行为,可能输出乱码或崩溃
    
  • strcpy/strcat不检查长度

    char buf[8];
    strcpy(buf, "supercalifragilistic");  // 溢出
    

成因
C的字符串管理完全依赖开发者手动保证结尾和空间,标准库函数本身不会自动帮你判断目标空间是否够用。


3. 规避方法与设计建议

  • 声明字符串数组时多留一个空间给'\0'
  • 初始化数组,如char buf[10] = {0};
  • 优先使用安全函数strncpy/strncat/snprintf,手动传递长度,防止溢出。
  • 操作后显式设置结尾,如buf[sizeof(buf)-1] = '\0';
  • 对于用户输入,始终做长度检查
  • 使用标准库的安全接口(如C11的strcpy_s等)或第三方安全库。

4. 代码示例

错误代码

#include <stdio.h>
#include <string.h>

void bad_example() {
    char name[8];
    strcpy(name, "0123456789abcdef"); // 超长拷贝
    printf("name: %s\n", name);       // 未定义行为
}

正确代码

#include <stdio.h>
#include <string.h>

void good_example() {
    char name[8];
    strncpy(name, "0123456789abcdef", sizeof(name) - 1);
    name[sizeof(name) - 1] = '\0'; // 确保结尾
    printf("name: %s\n", name);    // 安全输出
}

对比分析

  • 错误代码中strcpy完全不考虑目标空间,数据溢出,破坏栈上其他变量。
  • 正确代码用strncpy限制拷贝长度,并手动设置\0保证安全。

5. 底层原理

  • C字符串遍历依赖内存中第一个\0作为终止;没有\0,API会继续读下去,可能读到非法或敏感数据。
  • 溢出时,紧邻的变量、返回地址都可能被覆盖,造成严重后果。
  • 栈溢出攻击利用了溢出覆盖返回地址或函数指针,劫持程序流程。

6. 图示

在这里插入图片描述


7. 总结与建议

字符串操作是C语言安全事故的重灾区。永远不要假设C会自动为你加上\0或检查数组边界。
在声明和操作字符串时,要主动考虑结尾和空间,优先使用安全函数,手动设置结尾。代码审查时,字符串相关的越界和结尾问题必须作为重点。

实际开发建议:

“每次写字符串操作,都要问自己:‘内存够吗?结尾安全了吗?’”

公众号 | FunIO
微信搜一搜 “funio”,发现更多精彩内容。
个人博客 | blog.boringhex.top

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值