
🌊用有趣的言语来阐述苦涩难懂的代码世界,让每一个技术都充满风趣!
🔭个人主页: 散峰而望
🚀学习方向: C/C++等方向
📌专栏系列:
💬人生格言: 冀以尘雾之微,补益山海,荧烛末光,增辉岁月。
🎬博主简介



文章目录
- 前言
- [3. 字符数组](#3. 字符数组)
-
- [3.1 字符数组介绍](#3.1 字符数组介绍)
-
- [3.1.1 字符数组的初始化](#3.1.1 字符数组的初始化)
- [3.1.2 字符串长度 - strlen](#3.1.2 字符串长度 - strlen)
- [3.2 字符数组的输入](#3.2 字符数组的输入)
-
- [3.2.1 输入没有空格字符串](#3.2.1 输入没有空格字符串)
- [3.2.2 输入有空格的字符串](#3.2.2 输入有空格的字符串)
-
- [3.2.2.1 发现问题](#3.2.2.1 发现问题)
-
- [3.2.2.1.1 scanf 的表达式](#3.2.2.1.1 scanf 的表达式)
- [3.2.2.1.2 cin的方式](#3.2.2.1.2 cin的方式)
- [3.2.2.2 解决问题](#3.2.2.2 解决问题)
-
- [3.2.2.2.1 gets 和 fgets](#3.2.2.2.1 gets 和 fgets)
- [3.2.2.2.2 scanf](#3.2.2.2.2 scanf)
- [3.2.2.2.3 getchar](#3.2.2.2.3 getchar)
- [3.3 字符数组的输出](#3.3 字符数组的输出)
- [3.4 strcpy 和 strcat](#3.4 strcpy 和 strcat)
-
- [3.4.1 strcpy](#3.4.1 strcpy)
- [3.4.2 strcat](#3.4.2 strcat)
- [3.5 练习](#3.5 练习)
- 结语
前言
3. 字符数组
3.1 字符数组介绍
数组的元素如果是字符类型,这种数组就是字符数组,字符数组可以是一维数组,可以是二维数组(多维数组)。
接下来主要讨论一维的字符数组。
cpp
char arr1[5]; //一维数组
char arr2[3][5];//二维数组
C 语言中使用双引号括起来一串字符表示字符串 ,这种方式虽然在 C++ 中也是支持的,但是一般我们会将这种字符串称为 C 语言风格的字符串 。如果需要将一个 C 语言风格的字符串存储起来,就可以是字符数组。
3.1.1 字符数组的初始化
cpp
char a[10]; //字符数组的创建
字符数组的创建同一维数组、二维数组的创建就不再赘述,但是字符串数的初始化有2种方式,如下:
cpp
//方式1:直接使用字符串
char ch1[10] = "abcdef";
char ch2[] = "abcdef";//如果数组初始化的时候,数组的大小可以省略不写,数组大小会根据初始化内容来确定
//方式2 :挨个使用数组
char ch3[10] = {'a', 'b', 'c', 'd', 'e', 'f'};
char ch4[] = {'a', 'b', 'c', 'd', 'e', 'f'};

如果调试看一下 ch2 和 ch4 数组的内容,我们会明显的发现,数组ch2中多一个'\0'字符,这是因为字符串的末尾其实隐藏一个'\0'字符,这个'\0'是字符串的结束标志,在打印字符串的时候遇到'\0',打印结束。而 ch3 是因为限制了内存空间,剩余的 4 个空间默认初始化为 0。
当我们把字符串存放在一个字符数组中的时候,这时候也可以把字符数组当做字符串看到。

3.1.2 字符串长度 - strlen
字符数组中存放的着字符串,这个字符数组有自己的长度,也就是数组的元素个数,这个可以使用 sizeof 计算,那数组中存放的字符串的长度是多少?怎么来计算呢?其实C/C++中有一个库函数叫:strlen,可以求字符串的长度,其实统计的就是字符串中\0之前的字符个数。strlen需要的头文件是< cstring >。
语法如下:
cpp
size_t strlen ( const char * str );
//str - 指针,存放的是字符串的起始地址,从这个地址开始计算字符串的⻓度
代码演示:
cpp
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char arr[20] = "abcdef";
cout << "数组的⻓度:" << sizeof(arr)/sizeof(arr[0]) << endl;
cout << "字符串的⻓度:" << strlen(arr) << endl;
return 0;
}
演示结果:

如果是单个字符呢?
cpp
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char arr[] = {'a','b','c','d','e','f'};
cout << "数组的长度:" << sizeof(arr)/sizeof(arr[0]) << endl;
cout << "字符串的长度:" << strlen(arr) << endl;
return 0;
}
演示结果:


打印结果会不同,因为要找到 \0 的位置不同。
3.2 字符数组的输入
3.2.1 输入没有空格字符串
使用 scanf 函数和字符数组来实现:
cpp
#include<iostream>
int main()
{
char arr[20] = { 0 };
//输入
scanf("%s", arr);
//输出
printf("%s", arr);
return 0;
}
使用cin和字符数组来实现:
cpp
#include <iostream>
using namespace std;
int main()
{
char arr[20] = { 0 };
//输入
cin >> arr;
//输出
cout << arr << endl;
return 0;
}

上面两个代码都是将字符串读取后从数组的起始位置开始存放的,当然也可以指定位置位置存放,比如从数组的第二个元素的位置开始存放,如下代码:
cpp
#include <iostream>
using namespace std;
int main()
{
char arr[20] = { 0 };
//输入
cin >> arr + 1;//arr表示数组的起始位置,+1意思是跳过一个元素,就是第二个元素的位置
//可以通过调试观察一下arr的内容
cout << arr + 1;
return 0;
}

那么从第 n 个元素开始存放,就应该是 cin >> arr + n ;使用 scanf 函数也是一样的。
cpp
#include <cstdio>
int main()
{
char arr[20] = { 0 };
//输入
scanf("%s", arr+2);//从arr+2的位置开始存放
//输出
printf("%s", arr+2);//从arr+2的位置开始打印
return 0;
}
3.2.2 输入有空格的字符串
3.2.2.1 发现问题
前面我们讲解了 scanf 和 cin 读取不含空格的字符串,一切都正常,非常简单。
那当我们输入的字符串中有空格,会怎么样呢?
3.2.2.1.1 scanf 的表达式
代码如下:
cpp
#include <cstdio>
int main()
{
char arr[20] = {0};
//输入
scanf("%s", arr);
//输出
printf("%s", arr);
return 0;
}
演示结果:

这里特别说一下占位符 %s,它其实不能简单地等同于字符串。它的规则是,从当前第一个非空白字符开始读起,直到遇到空白字符(即空格、换行符、制表符等)为止。
因为 %s 的读取不会包含空白字符,所以无法用来读取多个单词,除非多个 %s 一起使用。这也意味着,scanf()不适合读取可能包含空格的字符串,比如书名或歌曲名。另外有一个细节注意一下,scanf() 遇到 %s 占位符,会在字符串变量末尾存储一个\0字符。
同时 scanf() 将字符串读入字符数组时,不会检测字符串是否超过了数组长度。所以,储存字符串时,很可能会超过数组的边界,导致预想不到的结果。为了防止这种情况,使用 %s 占位符时,可以指定读入字符串的最长长度,即写成 %[m]s,其中的 [m] 是一个整数,表示读取字符串的最大长度,后面的字符将被丢弃。
cpp
#include <cstdio>
int main()
{
char name[10];
scanf("%8s", name);
return 0;
}
上面示例中,name 是一个长度为 10 的字符数组,scanf() 的占位符%8s表示最多读取用户输入的 8 个字符,后面的字符将被丢弃,这样就不会有数组溢出的风险了。
3.2.2.1.2 cin的方式
cpp
#include <iostream>
using namespace std;
int main()
{
char arr[20] = { 0 };
//输入
cin >> arr;
//输出
cout << arr << endl;
return 0;
}
演示结果:

结果也是一样,没有任何区别。
其实cin在读取一个字符串的时候,在遇到空白字符的时候,就认为字符串结束了,不再继续往后读取剩余的字符,同时将已经读取到的字符串末尾加上 \0,直接存储起来。
3.2.2.2 解决问题
3.2.2.2.1 gets 和 fgets
使用gets函数的方式,这种方式能解决问题,但是因为gets存在安全性问题,C++11中取消了gets,给出了更加安全的方案:fgets。
cpp
char * gets ( char * str );
char * fgets ( char * str, int num, FILE * stream );
-
gets是从第一个字符开始读取,一直读取到 \n 停止,但是不会读取 \n,也就是读取到的内容中没有包含 \n ,但是会在读取到的内容后自动加上 \0。
-
fgets也是从第一个字符开始读取,最多读取 num-1 个字符,最后一个位置留给 \0,如果 num 的长度是远大于输入的字符串长度,就会一直读取到 \n 停止,并且会读取 \n,将 \n 作为读取到内容的一部分,同时在读取到的内容后自动加上 \0。
cpp
#include <cstdio>
//方案1
int main()
{
char arr[10] = {0};
gets(arr);
printf("%s\n", arr);
return 0;
}
//方法2
#include <cstdio>
int main()
{
char arr[10] = {0};
fgets(arr, sizeof(arr), stdin);
printf("%s\n", arr);
return 0;
}
演示结果:


上述两个程序,同样在运行起来后,在控制台窗口中输入:abc def,按回车,方案 1和方案 2 中 arr 数组的内容中差异如下:

提示:有 DevC++ 中使用 gets 函数,确实没有报错,但是在其他的IDE上,比如:VS2022 上直接报错,不允许使用 gets 函数。
所以在代码中还是慎用 gets 函数。

3.2.2.2.2 scanf
当然C语言中使用 scanf 函数其实也能做到读取带有空格的字符串,只是不常见而已。方式就是将 %s 改成 %[ ^\n ]s,其中在 % 和 s 之间加上了 [ ^\n ],意思是一直读取,直到遇到 \n,这样即使遇到空格也就不会结束了。这种方式读取,不会将 \n 读取进来,但是在读取到的字符串末尾加上 \0。
cpp
#include<cstdio>
int main()
{
char arr[10];
scanf("%[^\n]s", arr);
printf("%s", arr);
return 0;
}
演示结果:

3.2.2.2.3 getchar
有前面所学,我们知道 getchar 逐个字符读取,也是可以读取带有空格的字符串的。如果这一部分忘记的话可以移步来看:C/C++输入输出初级(一) (算法竞赛)
cpp
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
char arr[10];
int ch = 0;
int i = 0;
while((ch=getchar()) != '\n')
{
arr[i] = ch;
i++;
}
printf("%s\n", arr);
return 0;
}
演示结果:

至于为什么不用 cout << arr; 是因为这堆数组没有 \0 ,如果直接调用的话,会超出数组边界,打印烫烫烫的字样。

当然如果想要用 cout 打印也非常简单,代码如下:
cpp
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
char arr[10];
int ch = 0;
int i = 0;
while((ch=getchar()) != '\n')
{
arr[i] = ch;
i++;
}
arr[i] = '\0';//添加字符串终止符
cout << arr;
return 0;
}
演示结果:

3.3 字符数组的输出
- C 语言中可以在printf函数中使用%s占位符的方式,打印字符数组中的字符串。
- C++ 中使用cout,可以直接打印字符数组中的字符串内容。
- 也可以采用循环的方式逐个字符打印字符串的内容。
cpp
//方法1
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
char a[] = "hello world";
cout << a << endl;
printf("%s\n", a);
return 0;
}
//方法2
//单个字符的打印,直到\0字符,\0不打印
#include <iostream>
using namespace std;
int main()
{
char a[] = "hello world";
int i = 0;
while (a[i] != '\0')
{
cout << a[i];
i++;
}
cout << endl;
return 0;
}
//方法3
//单个字符打印,根据字符串⻓度来逐个打印
//strlen可以求出字符串的⻓度,不包含\0
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char a[] = "hello world";
int i = 0;
for (i = 0; i < strlen(a); i++)
{
cout << a[i];
}
cout << endl;
return 0;
}
3.4 strcpy 和 strcat
3.4.1 strcpy
使用字符数组可以存放字符串,但是字符数组能否直接赋值呢?
比如:
cpp
char arr1[] = "abcdef";
char arr2[20] = {0};
arr2 = arr1;//这样这节赋值可以吗?
就行整型数组中,我们说的过一样,这里也是不行的。那么如何将 arr1 中的字符串,拷贝到 arr2 中呢?其实 C/C++ 中有一个库函数叫 strcpy,可以完成。
cpp
char * strcpy ( char * destination, const char * source );
//destination - 是目标空间的地址
//source - 是源头空间的地址
//需要的头文件 <cstring>
代码演示:
cpp
#include <iostream>
using namespace std;
#include <cstdio>
#include <cstring>
int main()
{
char arr1[] = "abcdef";
char arr2[20] = {0};
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
演示结果:

3.4.2 strcat
有时候我们需要在一个字符的末尾再追加一个字符串,那字符数组能直接追加吗?比如:
cpp
char arr1[20] = "hello ";
char arr2[] = "world";
arr1 += arr2;//这样也是不行的
那怎么办呢?C/C++ 中有一个库函数叫 strcat,可以完成。
cpp
char * strcat ( char * destination, const char * source );
//destination - 是目标空间的地址
//source - 是源头空间的地址
//需要的头文件 <cstring>
代码演示:
cpp
#include <iostream>
using namespace std;
#include <cstdio>
#include <cstring>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
演示结果:

除了上面的两个字符串相关函数外,其实 C/C++ 中还提供了一些其他的函数,有兴趣的可以拓展学习:< cstring > (string.h)
3.5 练习
这里的题后期会讲,大家可以先练练。
结语
好了,C++数组的已经搞完了,不知道各位有没有将这个比较重要的数组 搞明白没有。下一篇我们将会对字符串进一步深入,希望各位能够继续学习,为冲刺国赛做准备。
往期回顾:
C++数组(一)(算法竞赛)
C++数组(二)(算法竞赛)
同时愿诸君能一起共渡重重浪,终见缛彩遥分地,繁光远缀天。

