C++:带你搞定数组的疑难杂症--缓冲区溢出;数组越界;为什们打印内容能超出数组长度?

Posted 敲代码的Messi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++:带你搞定数组的疑难杂症--缓冲区溢出;数组越界;为什们打印内容能超出数组长度?相关的知识,希望对你有一定的参考价值。


前言

先给大家看看一段测试:

	char s[10];
    cin>>s;
    cout<<"strlen(s):"<<strlen(s)<<endl;
    cout<<"sizeof(s):"<<sizeof(s)<<endl;
    cout<<s[12]<<endl;
    cout<<s;

输入:

012345678912345

输出:

下面带着疑问,我们来看看数组到底有几斤几两!


Ⅰ- 超出内容存入数组没有?

实际上并没有!!!

机智的你肯定又会问,那为什么打印字符数组的时候,溢出内容也能打印呢?

我们首先弄清楚 cin>>s 的工作原理。

  1. 我们在输入时,需要按下回车键,才能将数据送入缓冲区(输入队列),然后将缓冲区的前十个字符送到数组中。(若缓冲区不够10个,则数组中剩余用空字符补充)。但是此时,缓冲区还有过剩的内容,此时,我们称为:缓冲区溢出。

想了解更多的 cin输入流的知识,推荐大家看看这篇文章点这里…

再弄清楚 cout<<s 这句代码实际的工作原理。

  1. 数组名s 告诉了 cout ,字符数组中第一个元素的地址没有告诉数组的长度。cout会往下输出,直到遇到空字符停止。所以按数组名输出字符串就是遇到空字符— \\0结束。(我们通过键盘输入时,会自动在末尾加上 \\0)
    正是这一原因,所以打印的时候,我们会误以为感觉数组被扩容了一样。从sizeof(s)=10也能看出,数组并未被改变。
char s2="123\\096";
cout<<s2;

运行结果:

123

举一反三,为什们 strlen[s]=15呢?

我们同样要弄清楚 strlen[s] 的工作原理。

  1. 相信机智的你已经猜出来了,strlen和cout的工作原理极其相似。它是求字符串的长度,而不是数组的长度(不把空字符计算在内)。值得注意的是:它同样也是将空字符当作结束符
char s2="123\\096";
cout<<strlen(s2);

运行结果:

3

注:关于数组名的运用还不够了解的,可以参考参考这篇文章,点这里。。。。


Ⅱ- s[12] 错误还是正确

这一看就是数组越界了呀?这编译不报错吗?为啥打印结果好像还有点正确呢?

1.首先谈谈为啥编译不报错?

C++作为全世界运行效率最高的语言(rust?什们LJ玩意儿)。鲁迅曾说过,🐟和熊掌不可兼得。C++为了提高程序的运行效率,不会自动检查数组下标,只能由程序员自行检查

s[12] 的工作原理:

  1. 数组名只告诉了数组的地址信息,并没有长度信息,另外又加上C++不自动检查数组下标,所以程序可以运行成功。
  2. 如我们所遇见的,下标越界了,它会自动接着那块内存往后写,读。

2.数组下标为负时

正是因为不检查数组下标,所以数组下标可为负。

char s3[7]="123456";
char *p=s3+1;
cout<<p[-1];

运行结果:
1

在这里,我只是为了它输出正确的值,大家只需要知道它的逻辑关系就行。

3.危险的越界

我们往往正是因为 缓冲区溢出而导致的数组越界。为什们时刻都在说数组不能越界呢?

因为对于数组后面的内存是未知的。如果该内存没有值还好,但是有值的话,我们对其进行修改,后果是不可预估的。
数组越界是一种难以捕捉的错误,因为越界语句本身并不一定导致程序立即出错,可能会在某些情况下才会出现错误,导致程序不按照应当的次序运行的怪现象。这也要看各个系统下各个编译器的实现,可能可以继续访问,也可能直接报段错误。

以上是关于C++:带你搞定数组的疑难杂症--缓冲区溢出;数组越界;为什们打印内容能超出数组长度?的主要内容,如果未能解决你的问题,请参考以下文章

c++缓冲区溢出问题

尝试 malloc 结构数组,但堆缓冲区溢出

分配给结构中的静态数组时堆缓冲区溢出

__m256 数组上的 Visual Studio 2019 C6385 / C6386(缓冲区溢出警告)

与内存有关的那些事儿(数组分配空间不够,导致缓冲区溢出,从而strcpy会出现异常)

C++ 堆栈溢出初始化数组