C++中字符数组与字符串

导师教大一新生C++,安排我去做助教。期间一个学生问我一个问题,奈何近两年主要都是用python和Java,C++一些基础知识有些陌生了浪费了很多时间。这个问题主要是字符数组引起的,这里简单记录一下相关知识,最近也多复习一下C++,希望不要在课堂上误人子弟!
本文实验环境为:Window10 VS2019 x86编译模式下

1.字符串与字符数组

先说字符数组,有以下几种方式初始化:

int main(int argc, char** argv) {
	char cArr0[5] = { '\0' };
	char cArr1[5] = { 0 };
	char cArr2[5] = "abcd";
	char cArr3[5] = { 'a','b','c','d' };
	char cArr4[5] = { 'a','b','c','d','\0' };
	char cArr5[5] = { 'a','b','c','d' ,'e'};
	char cArr6[] = "abcde";
	char cArr7[] = { 'a','b','c','d','e' };
	char cArr8[5];
	memset(cArr8, 0, 5);
	//for 循环也可以初始化  这里略过
	cout << "strlen  cArr0:" << strlen(cArr0) << endl;
	cout << "strlen  cArr1:" << strlen(cArr1) << endl;
	cout << "strlen  cArr2:" << strlen(cArr2) << endl;
	cout << "strlen  cArr3:" << strlen(cArr3) << endl;
	cout << "strlen  cArr4:" << strlen(cArr4) << endl;
	cout << "strlen  cArr5:" << strlen(cArr5) << endl;
	cout << "strlen  cArr6:" << strlen(cArr6) << endl;
	cout << "strlen  cArr7:" << strlen(cArr7) << endl;
	cout << "strlen  cArr8:" << strlen(cArr8) << endl;
	return 0;
}

大家不妨在这里暂停一下,思考一下每一个的输出是什么。
在这里插入图片描述
对比一下结果,和你预想的一样?
下面我解释一下每一个结果的原因:
在这里插入图片描述
简单总结一下,什么情况下会自动添加’\0’。

char str[]="abcde";
或给字符串加上大括号:char str[]={"abcde"};char str[6]="abcde"

对于char str[10]={'a','b','c','d','e'};相当于char str[10]={'a','b','c','d','e','\0','\0','\0','\0','\0'}
但是如果后面初始化的长度等于字符数组的长度,则不会再添加\0,此时存在潜在错误。

char str[]={'1','2','3','4','5'};
这种方法定义时,系统不会自动在字符串的末尾加上字符串结束符;
此时用sizeof()函数可以正确求出其所占的内存大小;但用strlen()函数不能正确求出其长度,因为strlen是通过\0判断字符串结束的。
所以,采用该方法定义时,一般人为地加上\0,char str[]={'1','2','3','4','5', '\0'};
int main(int argc, char** argv) {
	char cArr0[5] = { '\0' };
	char cArr1[5] = { 0 };
	char cArr2[5] = "abcd";
	char cArr3[5] = { 'a','b','c','d' };
	char cArr4[5] = { 'a','b','c','d','\0' };
	char cArr5[5] = { 'a','b','c','d' ,'e'};
	char cArr6[] = "abcde";
	char cArr7[] = { 'a','b','c','d','e' };
	char cArr8[5];
	memset(cArr8, 0, 5);
	//for 循环也可以初始化  这里略过

	cout << "sizeof  cArr0:" << sizeof(cArr0) << endl;
	cout << "sizeof  cArr1:" << sizeof(cArr1) << endl;
	cout << "sizeof  cArr2:" << sizeof(cArr2) << endl;
	cout << "sizeof  cArr3:" << sizeof(cArr3) << endl;
	cout << "sizeof  cArr4:" << sizeof(cArr4) << endl;
	cout << "sizeof  cArr5:" << sizeof(cArr5) << endl;
	cout << "sizeof  cArr6:" << sizeof(cArr6) << endl;
	cout << "sizeof  cArr7:" << sizeof(cArr7) << endl;
	cout << "sizeof  cArr8:" << sizeof(cArr8) << endl;
	return 0}

在这里插入图片描述

sizeof的结果很容易理解,如下:
在这里插入图片描述
下面我们说字符串,这里的字符串不是string而是char*这样的字符串。
在说之前,先简单介绍下C和C++的内存区域划分:
一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另
一块区域。 - 程序结束后由系统释放。–>分别是data区,bbs区
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放–>coment区
5、程序代码区—存放函数体的二进制代码。–>code区
如果有兴趣想进一步了解,大家可以ELF文件格式。
在这里插入图片描述
在这里插入图片描述

2.编码方式

有了上面的基础,我们来看一下这个同学问的问题(大佬略过)。
在这里插入图片描述
首先,如果没有编码相关基础的话,我们分析str1是5个字节,str2是5个字节,后面我们将str1改为“哈哈哈哈”,似乎没什么问题??但是编译器为什么报告说栈中的str1周围内容被破坏了?
这里涉及到了C++的编码方式,我们自己编写CPP文件有自己的编码方式,我这里是utf-8,而程序中的字符也有自己的编码方式,注意这两者不一样!后者是有由具体的编译器决定的,是cpp中的字符在运行过程中的编码方式,也称为内码,前者也称为外码。
说了这么多,就是想说一句,在我的实验环境下(windows10 VS2019)c++中char类型的内码采用的是GBK方式,一个中文占2个字节,如下:
在这里插入图片描述
因此,当我们这里改变str1的值为“哈哈哈哈”后,相当于是将str1的空间变大了,因此破坏了其它变量的内容。这个问题你如果使用gcc来编译本身不会报错,VS这种IDE是为了尽最大可能帮你写出安全的程序,因此给你报错了。具体如下图:
在这里插入图片描述
在这里插入图片描述

既然说到了C++的内码编码方式,就多说一点:
C++中一个char占一个字节(8bit)。
中文字符,占用的是2个字节,即2个char。采用GBK编码。
英文字符,占用的是1个字节,即1个char。
宽字符wchar_t采用unicode字符集,编码方式为utf16
一个wchar_t占2个字节。16个bit位。
一个字符 占用一个 wchar_t,不管是中文字符还是英文字符。

了解了字符集相关知识后,我们怎么解决这个问题呢?下面介绍两种解决方案。
第一种方案:
在这里插入图片描述
第二种方案:
在这里插入图片描述
我们再扩展一下,如果程序是这个样子呢?
在这里插入图片描述
cin默认是以空格 换行 TAB为结尾,因此空格后面的内容无法被识别。
对于这种情况我们可以使用getline函数,它是以换行符为结束标志的。
在这里插入图片描述
2022.10.12日更新
之前写的东西还没有讲明白字符数组,今天有人问我一个相关问题,这里补充上。

int main(int argc, char** argv) {
	
	char h[] = { '李','李',0 };
	/*cout << sizeof(h) << endl;
	cout << strlen(h) << endl;
	cout << h << endl;*/
	char t[] = "李李";
	cout << h << endl;
	cout << t << endl;
	return 0;

问题是这样的,为什么两种的方式打印结果也不一样呢?
在这里插入图片描述
从这里可以看出h的字节数是3 而t是5,这其实就是两种初始化方式的区别,第一种初始化时每个单引号’'中的字符占一个字节,后一种方式初始化时会计算整体占据字节数,因此出现了这种差异。下面结合GBK编码集感受一下具体差异。
“李”的GBK编码是C0EE,因为第一种方式会将它截断为一个字节,因此保留EE,因此h保存的3个字节是 EE EE 00,打印时\0不打印,EEEE按照GBK解码正好是铑。
可以去这个网站自己动手试一下:
http://www.mytju.com/classcode/tools/encode_gb2312.asp

  • 13
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可怕的竹鼠商

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值