C++字符串操作中的陷阱

2024-09-06 08:28
文章标签 c++ 操作 字符串 陷阱

本文主要是介绍C++字符串操作中的陷阱,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

休对故人思故国,且将新火试新茶。诗酒趁年华。

                                                                            ——《望江南·超然台作》【宋】苏轼

目录

正文:

首先我们要明白出现问题的原因:

1. 缓冲区溢出

2. 错误的字符串声明方式

3. 缺乏对NULL指针的检查

 解决方案:

下期预告:C++字符串中的string类操作


上期的最后我们抛出了一个新的问题,那就是如果我们发现目标对象的内存已经不足但我们还是将资源对象拼接到目标对象那里,那么在目标内存那里的内存会有什么变化,会对我们的程序有什么危害。现在就让我们看一看:

正文:

我们来看一下代码实例:

#include<iostream>
using namespace std;
#include<string.h>
const int MAX=14;
int main()
{
char s[MAX]={0};
char n[]={"hello"};
chr m[]={"wello"};
char x[]={"only you can do it "}//1
cout<<strcpy(s,n);
//2
cout<<strcpy(s,m,2);
//3
cout<<strcat(s,m);
//4
cout<<strcat(s,x);//新加入的实例int unsigned len=strlen(s);
for(int i=0;i<strlen(s);++i)
{
cout<<s[i]<<endl;
}
return 0;
}

在昨天的代码中我加入了一个新的字符数组,然后将这个字符数组拼接到s数组中,然后输出这个数组。这个时候问题就出现了,我们可以看到在第三步结束时我们的数组s所剩的存储空间还有4个位,但是我们的资源数组(x)的长度远远超过4,所以这个时候就会出现一个问题,那就是数据溢出。我们要知道数据溢出之后就会向后填充溢出的部分,但是这个数组之后的空间存储了其他的数据。这就是说溢出的数据会替换掉下一个位置原本的数据。(比如:我们s数组之后存储的是数组a,那么s数组溢出的数据就会侵占原本a数组中元素的位置,导致a数组的存储内容出现问题。)

这个问题十分的严重,因为一不小心就会导致整个项目出现bug无法运行,尤其可怕的是这种溢出系统是不会报错的,也就是说会在我们不知道的情况下出错。如果是个巨大的项目,那么就是一个十分棘手的bug。


首先我们要明白出现问题的原因:

strcat这类的函数在C语言等编程语言中,用于将两个字符串连接起来,即将源字符串(src)拼接到目标字符串(dest)的末尾。然而,这类函数存在安全缺陷,主要原因包括:

1. 缓冲区溢出
  • 根本原因:strcat函数没有检查目标字符串(dest)的空间是否足够以容纳源字符串(src)。如果dest所指向的缓冲区大小不足以容纳两个字符串连接后的结果,那么strcat会继续在dest的原始内存区域之后写入数据,直到src的末尾。这会导致缓冲区溢出,可能覆盖相邻内存区域的数据,引发程序崩溃或安全漏洞。
  • 危害:缓冲区溢出是许多安全漏洞的根源,攻击者可以利用这一漏洞执行任意代码、提升权限或绕过安全机制。
2. 错误的字符串声明方式
  • 在使用strcat时,如果目标字符串是通过指针指向一个字符串常量(如char *dest = "initial";),则尝试修改这个字符串(即拼接新内容)将导致未定义行为,因为字符串常量通常存储在只读内存区域。
  • 正确的做法是使用字符数组(如char dest[SIZE] = "initial";)来确保有足够的空间进行字符串操作,并且该数组是可修改的。
3. 缺乏对NULL指针的检查
  • 如果strcat的任一参数为NULL,其行为是未定义的。在实际使用中,应该检查指针的有效性,以避免潜在的空指针解引用错误。(主要原因)
 解决方案:

所以C++有了这类函数的升级版,更加的安全:

strnlen_s,strcpy_s,strncpy_s,strcat_s等几个函数

请看优化后的代码:

/*#include<iostream>
using namespace std;
#include<string.h>
#include<cstring>
const int MAX=12;
int main()
{
char s[MAX]={0};
char n[]={"hello"};
char m[]={"wello"};
//cout<<strcpy(s,n);
cout<<strcpy_s(s,MAX,n) << endl;
//cout<<strcpy(s,n,2);
cout<<strcpy_s(s,MAX,n,2);
//cout<<strcat(s,m);
cout<<strcat_s(s,MAX,m);unsigned int len=strlen(s);
for(int i=0;i<len;++i)
{
cout<<s[i]<<endl;
}
return 0;
}
*/#include<iostream>
using namespace std;
#include<string.h>
#include<cstring>const int MAX=12;int main()
{char s[MAX]={0};char n[]={"hello"};char m[]={"wello"};// 使用strncpy来安全地复制字符串strncpy(s, n, MAX);s[MAX-1] = '\0'; // 确保字符串以null终止cout << s << endl;// 使用strncat来安全地连接字符串strncat(s, m, MAX-strlen(s)-1);//求出剩余内存的长度s[MAX-1] = '\0'; // 确保字符串以null终止cout << s << endl;unsigned int len = strlen(s);for(unsigned int i=0; i<len; ++i){cout << s[i] << endl;}return 0;
}

一些第三方库提供了额外的安全函数,这些函数通过检查边界和提供其他保护措施来增强C的安全性。例如,Microsoft的C运行时库(CRT)提供了安全版本的函数,如strcpy_s。(部分编译器可能无法使用)

这里说明一下,代码中其实我列了两种的方式,第一种可能会在编译器中无法通过,所以我又写了第二种方式,这种方式也是可以的,不过就是要自己对结尾加上一个结束符,这就考验编写者的个人细心程度了,所以这种办法也不是很常用,因此在C++中又诞生了一种更简单的,更安全的方法那就是string类里的函数(比如:strlcpy)。

这些代码很简单没什么要特殊注意的,自己记住这些格式就没问题,只需要记住最原始的strcpy之类的函数是没有自动停止的功能,会产生一些隐藏的bug,有这个意识就可以了,即使对此有了完整的技术可以杜绝这种隐患,但是我们还是要保持这种意识,对我们写代码有帮助。

下期我们就来讲一下string类里的相关操作。

🆗到这里,这篇关于:C++字符串操作中的陷阱就说完了,求一个免费的赞,感谢阅读。

下期预告:C++字符串中的string类操作

这篇关于C++字符串操作中的陷阱的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1141504

相关文章

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.