C++数组(三)(算法竞赛)


🌊用有趣的言语来阐述苦涩难懂的代码世界,让每一个技术都充满风趣!

🔭个人主页: 散峰而望

🚀学习方向: 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 );
  1. gets是从第一个字符开始读取,一直读取到 \n 停止,但是不会读取 \n,也就是读取到的内容中没有包含 \n ,但是会在读取到的内容后自动加上 \0。

  2. 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 字符数组的输出

  1. C 语言中可以在printf函数中使用%s占位符的方式,打印字符数组中的字符串。
  2. C++ 中使用cout,可以直接打印字符数组中的字符串内容。
  3. 也可以采用循环的方式逐个字符打印字符串的内容。
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

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

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 练习

这里的题后期会讲,大家可以先练练。

  1. 自动修正
  2. 统计数字字符个数
  3. 基因相关性
  4. 验证子串
  5. 找第一个只出现一次的字符
  6. 输出亲朋字符串

结语

好了,C++数组的已经搞完了,不知道各位有没有将这个比较重要的数组 搞明白没有。下一篇我们将会对字符串进一步深入,希望各位能够继续学习,为冲刺国赛做准备。

往期回顾:
C++数组(一)(算法竞赛)
C++数组(二)(算法竞赛)

同时愿诸君能一起共渡重重浪,终见缛彩遥分地,繁光远缀天

相关推荐
q***95221 小时前
SpringMVC 请求参数接收
前端·javascript·算法
4***14902 小时前
C++在系统中的编译优化
开发语言·c++
mit6.8242 小时前
[HomeKey] 握手协议 | NFC协议处理器
c++
田姐姐tmner2 小时前
Python切片
开发语言·python
oioihoii2 小时前
C++程序执行起点不是main:颠覆你认知的真相
开发语言·c++
初级炼丹师(爱说实话版)2 小时前
多进程与多线程的优缺点及适用场景总结
算法
hetao17338372 小时前
2025-11-25~26 hetao1733837的刷题记录
c++·算法
周杰伦fans2 小时前
C# 中的**享元工厂**模式
开发语言·数据库·c#
历程里程碑2 小时前
各种排序法大全
c语言·数据结构·笔记·算法·排序算法