当前位置: 首页 > news >正文

[C/C++内存安全]_[中级]_[安全处理字符串]

场景

  1. 在现代C++开发指南出来后,并不建议使用C的某些内存不安全的字符串处理函数。那么有哪些函数不安全?

说明

  1. 内存安全方面,肯定是要向Rust看齐的。使用标准std::string字符串类,很大情况能避免缓冲区溢出问题。

  2. 如果旧项目里有用到以下的这些C字符串函数,那么最好用std::string改写吧。

    • strcpy: 不要使用.strcpy,目标缓冲区没有设置最大接收的大小,容易造成缓冲区溢出。strcpy最后会复制源缓冲区的\0到目标缓冲区末尾,前提是目标缓冲器有足够的大小。

    • strcat: 不要使用. 只是在目标缓冲区的0位置开始添加新的字符串。目标缓冲区没有设置最大接收的大小,容易造成缓冲区溢出。strcat最后会复制源缓冲区的\0到目标缓冲区末尾,前提是目标缓冲器有足够的大小。

    • sprintf: 不要使用. 目标缓冲区没有设置最大接收的大小,容易造成缓冲区溢出。sprintf最后会复制源缓冲区的\0`到目标缓冲区末尾,前提是目标缓冲器有足够的大小。

    • strncpy: 不要使用. 目标缓冲区没有设置最大接收的大小,容易造成缓冲区溢出。·strncpy·最后不会复制一个结束符到目标缓冲区,如果目标缓冲区不是以0结尾,那么在读取目标缓冲区时也会读取越界。

    • strncat: 不要使用.strncat 目标缓冲区没有设置最大接收的大小,容易造成缓冲区溢出。·strncat·最后会复制源缓冲区的\0到目标缓冲区末尾,前提是目标缓冲器有足够的大小。

  3. strcpy,strcat,strncpystrncat可以使用std::string代替;sprintf可以使用snprintf或者stringstream代替。

例子

  1. 以下例子使用这些危险的C函数处理字符串,并使用std::stringstringstream来替换之。
#include <iostream>
#include <string.h>
#include <string>
#include <iostream>
#include <sstream>
#include <iomanip>using namespace std;void TestUnsafeCStringFunc()
{const int kBufSize = 32;char buf[kBufSize] = {0};string str;// 30个字符const char kStr[] = "http://blog.csdn.net/infoworld";constexpr int kStrSize = sizeof(kStr) - 1;// 1. 不要使用.`strcpy`,目标缓冲区没有设置最大接收的大小,容易造成缓冲区溢出。//  -- `strcpy`最后会复制源缓冲区的`\0`到目标缓冲区末尾,前提是目标缓冲器有足够的大小。strcpy(buf, kStr);cout << "strcpy: " << buf << endl;//  -- 使用`string`的代替.str.append(kStr);cout << "string: " << str << endl;// 2. 不要使用.同`strcpy`,只是在目标缓冲区的`0`位置开始添加新的字符串。目标缓冲区没有设置最大接收的大小,容易造成缓冲区溢出。//  -- `strcat`最后会复制源缓冲区的`\0`到目标缓冲区末尾,前提是目标缓冲器有足够的大小。memset(buf, 0, sizeof(buf));strcat(buf, kStr);strcat(buf, "/");cout << "strcat: " << buf << endl;//  -- 使用`string`的代替.str.clear();str.append(kStr);str.append("/");cout << "string: " << str << endl;// 3. 不要使用. 目标缓冲区没有设置最大接收的大小,容易造成缓冲区溢出。//  -- `sprintf`最后会复制源缓冲区的`\0`到目标缓冲区末尾,前提是目标缓冲器有足够的大小。sprintf(buf, "%d-%.2d-%.2d", 2025, 7, 22);cout << "sprintf: " << buf << endl;// -- 方法1:使用`stringstream`代替stringstream ss;ss << 2025 << "-" << std::setw(2) << std::setfill('0') << 7 << "-"<< std::setw(2) << std::setfill('0') << 22;str = ss.str();cout << "stringstream: " << str << endl;// -- 方法2: 使用`snprintf`代替.// -- 安全,指定目标缓冲区大小,最多指定目标缓冲区大小`-1`个字符被写入,之后会添加一个结束符。// -- 如果`buf`为`NULL`,并且目标缓冲区大小为`0`时,返回写源字符串所需要的总大小, 不包括结束符。snprintf(buf,sizeof(buf),"%d-%.2d-%.2d", 2025, 7, 22);cout << "snprintf: " << buf << endl;auto number = snprintf(NULL,0,"%d-%.2d-%.2d", 2025, 7, 22);cout << "snprintf number: " << number << endl;auto myBuf = (char*)malloc(number+1);sprintf(myBuf,"%d-%.2d-%.2d", 2025, 7, 22);cout << "myBuf: " << myBuf << endl;free(myBuf);// 4. 不要使用. `strncpy` 目标缓冲区没有设置最大接收的大小,容易造成缓冲区溢出。// -- ·strncpy·最后不会复制一个结束符到目标缓冲区,如果目标缓冲区不是以`0`结尾,那么在读取目标缓冲区时也会读取越界。memset(buf, 0, sizeof(buf));strncpy(buf, kStr, kStrSize);cout << "strncpy: " << buf << endl;// -- 同上,还是使用`std::string`代替。// 5. 不要使用. `strncat` 目标缓冲区没有设置最大接收的大小,容易造成缓冲区溢出。// -- ·strncat·最后会复制源缓冲区的`\0`到目标缓冲区末尾,前提是目标缓冲器有足够的大小。buf[kBufSize - 1] = '1'; // 测试最后添加一个`1`字符,调用`strncat`后会使用`0`把它覆盖。strncat(buf, "/", 1);cout << "strncat: " << buf << endl;// -- 同上,还是使用`std::string`代替。
}int main()
{std::cout << "Hello World!\n";std::cout << "=========== TestUnsafeCStringFunc =========!\n";TestUnsafeCStringFunc();
}

输出

Hello World!
=========== TestUnsafeCStringFunc =========!
strcpy: http://blog.csdn.net/infoworld
string: http://blog.csdn.net/infoworld
strcat: http://blog.csdn.net/infoworld/
string: http://blog.csdn.net/infoworld/
sprintf: 2025-07-22
stringstream: 2025-07-22
snprintf: 2025-07-22
snprintf number: 10
myBuf: 2025-07-22
strncpy: http://blog.csdn.net/infoworld
strncat: http://blog.csdn.net/infoworld/

参考

  1. strcpy

  2. strcat

  3. strncpy

  4. strncat

  5. sprintf,snprintf

  6. stringstream

  7. string

  8. C++11语言特性和标准库

  9. 如何实现std::string自己的Format(sprintf)函数

  10. 避免使用wsprintf函数

  11. setw

  12. setfill

http://www.dtcms.com/a/294393.html

相关文章:

  • ctfshow pwn40
  • 保护板测试仪:守护电池安全的“幕后卫士”
  • 关于SPring基础和Vue的学习
  • Docker 容器中的 HEAD 请求缺失 header?从 Content-MD5 缺失聊起
  • 超声原始数据重构成B扫成像的MATLAB实现
  • 【AI News | 20250722】每日AI进展
  • now能减少mysql的压力吗
  • 【Android】用 ViewPager2 + Fragment + TabLayout 实现标签页切换
  • linux性能调整和故障排查
  • LeetCode热题100--24. 两两交换链表中的节点--中等
  • Linux文件——Ext2文件系统(3)_软硬链接
  • Ubuntu 1804 编译ffmpeg qsv MediaSDK libva 遇到的问题记录
  • #Linux内存管理# 详细介绍madvise函数的工作原理
  • Elasticsearch(ES)安装
  • 分布式电商系统:缓存策略、负载均衡与容灾方案
  • 解决 Electron 中 window.open 打开新窗口的各种“坑”
  • Python 程序设计讲义(6):Python 的基本用法——运算符与表达式
  • API 汇总:ONLYOFFICE 文档最近更新
  • 背包DP之0/1背包
  • 11-1 浅层神经网络及计算前向传播
  • 局部重要性注意力LIA,通过区域重要性图与门控机制实现高阶信息交互,自适应增强有用特征、抑制冗余信息,平衡模型性能与效率。
  • VR-Doh: 革新3D建模的虚拟现实体验
  • DPVR亮相青岛品牌日,崂山科创力量引领AI眼镜新浪潮
  • 基于PLC的轨检小车控制器设计
  • .NET-键控服务依赖注入
  • 【实战】Dify从0到100进阶--文档解读(13)API前端再开发
  • 苍穹外卖DAY11
  • 【LeetCode数据结构】栈和队列的应用——设计循环队列问题详解
  • 【后端】FastAPI的Pydantic 模型
  • Excel 将数据导入到SQLServer数据库