环境
- Microsoft Visual Studio Community 2022
- Windows 11 家庭中文版
笑话
小明在超市买了3瓶汽水,他先打开第0瓶汽水,咕咚咕咚喝光了,接着打开第1瓶汽水,又咕咚咕咚喝光了,然后又打开第2瓶汽水,咕咚咕咚喝光了,然后又打开第3瓶汽水,刚喝了一口,突然喊道:“烫烫烫烫烫烫烫!”
解释
在Visual Studio中,未分配的内存空间,其初始值用 0xCC
(1个字节)填充,而查询编码表可知, 0xCCCC
(2个字节)代表字符 烫
。
如果转换为10进制,比如int数值,int是4个字节,也就是说,把 0xCCCCCCCC
(4个字节)转换为10进制,其值是 -858993460
。
在该笑话中,小明买了3瓶汽水,假设使用的是数组,那么能访问的数组下标为0、1、2,如果访问下标3,显然越界了。
在C语言中,对字符串的处理,稍不注意,就很容易出现越界的情况,而且编译和运行可能都不会报错,所以要格外小心。具体来讲,字符串是以 \0
结尾的,也就是说,在访问字符串的时候, \0
相当于一个哨兵,找到这里,就知道字符串结束了,否则会一直找下去,产生不可预知的结果。
测试
准备
在Visual Studio中创建一个新项目,选择 Empty Project
:
右键单击Source Files,Add -> New Items… ,创建 Test0527.cpp
文件:
测试1
打开 Test0527.cpp
文件,编辑如下:
#include <iostream>
using namespace std;
int main() {
char str[4];
str[0] = 'a';
str[1] = 'b';
str[2] = 'c';
str[3] = 0;
cout << str << endl;
}
这是正确的用法,要想使用字符数组来存储字符串 abc
,数组长度应该是4而不是3,最后一个元素 str[3]
要填入 \0
,表示字符串结束。
运行程序,结果如下:
测试2
如果忘了最后需要一个 \0
,而把字符数组的长度设置为3,则在打印 str
时,依次访问到 a
、 b
、 c
之后,并不会结束,而是会继续访问下去。如果接下来是未分配的内存空间,即 0xCCCCCCCCCCCCCCCCCCCCCCCC...
,就会出现 烫烫烫...
的字样。
修改程序如下:
#include <iostream>
using namespace std;
int main() {
char str[3];
str[0] = 'a';
str[1] = 'b';
str[2] = 'c';
cout << str << endl;
}
运行程序,结果如下:
注:本例中,数组可以直接初始化:
char str[3] = {'a', 'b', 'c'};
要想查看其int值,可以用一个int指针指向未初始化的元素,比如 str[3]
(注:编译器会发现此处越界,可以使用 str + 3
指针):
int* p = (int*)(str + 3);
cout << *p << endl;
其输出结果为 -858993460
。
测试3
实际上,静态分配的内存,如果没有初始化,其初始值也是 0xCC
。
修改程序如下:
#include <iostream>
using namespace std;
int main() {
char str[3];
cout << str << endl;
}
运行程序,结果如下:
其它
动态分配且未初始化的内存
对于动态分配的内存,如果没有初始化,其初始值是 0xCD
,转换为中文字符为 屯
( 0xCDCD
),其4字节( 0xCDCDCDCD
)对应的int值为 -842150451
。
编辑程序如下:
#include <iostream>
using namespace std;
int main() {
char* p = new char[100];
p[0] = 'a';
p[1] = 'b';
p[2] = 'c';
cout << p << endl;
delete[] p;
}
运行程序,结果如下:
要想查看其int值,可以用一个int指针指向未初始化的元素,比如 p[3]
:
int* p2 = (int*)(p + 3);
cout << *p2 << endl;
其输出结果为 -842150451
。
动态分配且已释放的内存
一块动态分配的内存,在释放掉之后,系统会用 0xDD
来填充。此时,如果又去访问它,则转换为中文字符为 葺
( 0xDDDD
),其4字节( 0xDDDDDDDD
)对应的int值为 -572662307
。
编辑程序如下:
#include <iostream>
using namespace std;
int main() {
char* p = new char[100];
char* p2 = p;
delete[] p;
cout << p2 << endl;
}
运行程序,结果如下:
要想查看其int值,可以用一个int指针指向 p
(在释放 p
之前),或者指向 p2
:
int* p3 = (int*)p2;
cout << *p3 << endl;
上述代码放在 p
释放之后,其输出结果为 -572662307
。