C++微基础备战蓝桥杯之数组篇10.1
#前言
uu们,我开始有点懒散了,今天开始写才发现已经9天没有学习了,~||~,言归正传,接下来高强度一点,看能不能补回来,这一篇大概写c++数组
一. ⼀维数组
数组是⼀组相同类型元素的集合;
从这个概念中我们就可以发现2个有价值的信息:
1.数组中存放的是1个或者多个数据,但是数组元素个数不能为0。
2• 数组中存放的多个数据,类型是相同的。
数组分为⼀维数组和多维数组,多维数组⼀般⽐较多⻅的是⼆维数组。
1. ⼀维数组的创建和初始化
⼀维数组是最常⻅的,通常⽤来存放⼀组数据,⼀维数组是⼀块连续的空间。
1.1 数组创建
⼀维数组创建的基本语法如下:
type arr_name[常量值]
存放在数组的值被称为数组的元素,数组在创建的时候需要指定数组的⼤⼩和数组的元素类型。
type 指定的是数组中存放数据的类型,可以是: 定义的类型 char 、 short 、 int 、 float 等,也可以⾃ arr_name 是数组的名字,这个名字可以⾃定义,根据实际情况,起的有意义就⾏。
•[] 中的常量值是⽤来指定数组的⼤⼩的,数组的⼤⼩是根据实际的需求指定就⾏。在算法竞赛中 为了为了保证不越界访问,往往会多开辟⼀些空间,后期题⽬中会讲到。
可以使⽤ const int N = 100 的⽅式定义常量,来指定数组的⼤⼩。
例如: int arr[N] ;
⽐如:我们现在想存储某个班级的20⼈的数学成绩,那我们就可以创建⼀个数组,如下:
#include <iostream>
using namespace std;const int N = 20;
int main()
{int math[N];return 0;}
1.12.数组的初始化
有时候,数组在创建的时候,我们需要给定⼀些初始值,这种就称为初始化的。
那数组如何初始化呢?数组的初始化⼀般使⽤⼤括号,将数据放在⼤括号中。
#include <iostream>
using namespace std;int main()
{int arr[5] = {1,2,3,4,5};//完全初始化、int arr2[6] = {1};//不完全初始化 //错误示范
// int arr3[3] = {1,2,3,4,5}return 0;}
1.2. ⼀维数组元素的访问
数组是有下标的,从0开始 ,可以使用下标引用操作符来实现访问
#include <iostream>
using namespace std;int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};//数组是有下标的,从0开始 ,可以使用下标引用操作符来实现访问 arr[3] = 4;// 其中的arr[3]就是下标引用操作符 return 0;}
求数组元素个数
#include <iostream>
using namespace std;int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};cout << sizeof(arr) << endl;//--40//这里使用的是字节,这里的是整型,一个整型占4个字节//则cout << sizeof(arr) / sizeof(arr[0]) << endl;//所有元素字节长度 / 一个元素字节长度//得出元素个数 return 0;}
3. ⼀维数组元素的打印
接下来,如果想要访问整个数组的内容,那怎么办呢?
只要我们产⽣数组所有元素的下标就可以了,那我们使⽤for 循环产⽣所有的下标,接下来使⽤下标访问就⾏了。
#include <iostream>
using namespace std;int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int i = 0;for(i = 0; i < 10; i++){cout << arr[i] << " ";}cout << endl;return 0;}
由打印元素个数方式可将其优化为
#include <iostream>
using namespace std;int main()
{int arr[5] = {0};int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);for(i = 0;i < sz;i++){cin >> arr[i]; }for(i = 0; i < sz; i++){cout << arr[i] << " ";}cout << endl;return 0;}
4. 范围for
打印数组元素除了可以使⽤之前讲过的三种循环外,还有⼀个更⽅便的⽅式,使⽤范围for
范围for语法:
for(类型 变量名 : 数组名 )
但是范围for只能c++11支持使用
比如devC++就不能使用,那如何配置环境呢?
在devC++中,菜单中,点击:工具->编译选项,打开
勾选【编译时加入一下命令】,然后在下方的编译框中加入:
-std=c++11
其他代码块
-std=c++14
-std=c++17
-std=c++20
eg
#include <iostream>
using namespace std;int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10};for(int e : arr){e = 1;}for(int e : arr){cout << e << " ";}//输出1 2 3 4 5 6 7 8 9 10 return 0;}
范围for只能打印数组,不能更改数组
5. auto 关键字
auto 的主要⽤途是让编译器⾃动推导出变量的类型的,
⽐如:
#include <iostream>
using namespace std;int main()
{auto a = 3.14;auto b = 100;auto c = 'x';cout << a << endl;cout << b << endl;cout << c << endl;return 0;}
调试时能看到a,b ,c ,类型
不想给其明确指定类型,或者不确定其字符类型就可以使用auto
范围for也经常使用auto类型
for(auto e : arr){e = 1;}
6. memset设置数组内容
函数原型介绍
void * memset ( void * ptr, int value, size_t num );参数解释:
ptr -- 指针:指向了要设置的内存块的起始位置
value -- 要设置的值
num -- 设置的字节个数
#include <iostream>
#include <cstring>
using namespace std;int main()
{char arr[] = "hello world";cout << arr << endl;//对于整型数组不能直接这样打印 memset(arr,'x',5);//使用需要头文件 #include <cstring> cout << arr << endl;//打印出 xxxxx world return 0;
}
memset 是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容,需要头⽂件
#include <cstring>
一般使用memset来设置整型数组时都设置为0,其他数字容易出错
#include <iostream>
#include <cstring>
using namespace std;int main()
{int arr[5] = {1,2,3,4,5};memset(arr,0,20);for(int e : arr){cout << e << " ";}cout << endl;//输出 0 0 0 0 0 return 0;
}
比如
#include <iostream>
#include <cstring>
using namespace std;int main()
{int arr[5] = {1,2,3,4,5};memset(arr,1,20);for(int e : arr){cout << e << " ";}cout << endl;//输出 16843009 16843009 16843009 16843009 16843009return 0;
}
7. memcpy拷⻉数组内容
在使⽤数组的时候,有时候我们需要将数组a的内容给数组b,⽐如:
int a[10] = {1,2,3,4,5,6,7,8,9,10};int b[10] = {0};
怎么做呢?直接赋值是错误的
数组名是地址,地址是一个常量的值,常量不可被修改
其实C++中有⼀个库函数 memcpy 可以做数组内容的拷⻉,当然 memcpy 其实是⽤来做内存块的拷⻉ 的,当然⽤来做数组内容的拷⻉也是没问题的。
函数原型如下:
#include <cstring>void * memcpy ( void * destination, const void * source, size_t num );//destination -- 目标空间的起始地址//source -- 源数据空间的起始地址
//num -- 拷贝的数据的字节个数
eg
#include <iostream>
#include <cstring>
using namespace std;int main()
{int a[10] = {1,2,3,4,5,6,7,8,9,10};int b[10] = {0};memcpy(b, a, 10 * sizeof(int));for(int e: b){cout << e << " ";}//打印出1,2,3,4,5,6,7,8,9,10 return 0;}
二. ⼆维数组
1. ⼆维数组的概念
前⾯学习的数组被称为⼀维数组,数组的元素都是内置类型的,如果我们把⼀维数组做为数组的元 素,这时候就是⼆维数组,⼆维数组作为数组元素的数组被称为三维数组,⼆维数组以上的数组统称 为多维数组。
2. ⼆维数组的创建和初始化
2.1.创建
那我们如何定义⼆维数组呢?语法如下:
type arr_name[常量值1][常量值2];
例如:int arr[3][5];double data[2][8];
#include <iostream>
using namespace std;int main()
{int arr1[3][5] = {1,2};int arr2[3][50] = {1, 2, 3, 4, 5, 6};return 0;
}
下⾯解释⼀下上述代码中的⼀些关键信息:
• 3 表⽰数组有 3 ⾏
• 5 表⽰每⼀⾏有 5 个元素
• int 表⽰数组的每个元素是整型类型
• arr 是数组名,可以根据⾃⼰的需要指定名字
data 数组意思基本⼀致。
2.2.初始化
在创建变量或者数组的时候,给定⼀些初始值,被称为初始化。 那⼆维数组如何初始化呢?像⼀维数组⼀样,也是使⽤⼤括号初始化的。
#include <iostream>
using namespace std;int main()
{//不完全初始化int arr1[3][5] = {1,2};int arr2[3][5] = {0};//完全初始化int arr3[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};//按行初始化 int arr4[3][5] = {{1,2},{3,4},{5,6}};//如果数组初始化了,数组在指定??的时候可以省略?,?不能省略列int arr5[][5] = {1,2,3};int arr6[][5] = {1,2,3,4,5,6,7};int arr7[][5] = {{1,2}, {3,4}, {5,6}};return 0;
}
2.3.二维数组下标
当我们掌握了⼆维数组的创建和初始化,那我们怎么使⽤⼆维数组呢? 比特就业课 其实⼆维数组访问也是使⽤下标的形式的,⼆维数组是有⾏和列的,只要锁定了⾏和列就能唯⼀锁定 数组中的⼀个元素。
C/C++规定,⼆维数组的⾏是从 0 开始的,列也是从 0 开始的,如下所⽰:
int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
eg
#include <iostream>
using namespace std;int main()
{int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};cout << arr[2][4] << endl;//输出7 return 0;}
3. ⼆维数组的元素的访问
#include <iostream>
using namespace std;int main()
{int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };for (int i = 0; i < 3; i++){for (int j = 0; j < 5; j++) {cout << arr[i][j] << " ";}cout << endl;}//输出//1 2 3 4 5//2 3 4 5 6 //3 4 5 6 7 return 0;
}
4. memset设置⼆维数组
memset 是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。 下⾯演⽰两个例⼦,使⽤ memset 来设置⼆维数组的内容
#include<iostream>
#include <cstring>
using namespace std;
int main()
{char arr[10][10]; memset(arr, 'x', sizeof(arr));
//将二维整型数组中每个字节都设置为xfor(int i = 0; i < 10; i++){for(int j = 0; j < 10; j++){cout << arr[i][j] << " ";}cout << endl;}return 0;
}
memset 在⼆维数组中的⽤法同⼀维数组,同样也需要注意避免给整型赋⾮ 0 值
三. 字符数组
数组的元素如果是字符类型,这种数组就是字符数组,字符数组可以是⼀维数组,可以是⼆维数组 (多维数组)。 接下来主要讨论⼀维的字符数组。
char arr1[5]; //二维数组char arr2[3][5];//二维数组
C语⾔中使⽤双引号括起来⼀串字符表⽰字符串,这种⽅式虽然在C++中也是⽀持的,但是⼀般我们 会将这种字符串称为C语⾔⻛格的字符串。如果需要将⼀个C语⾔⻛格的字符串存储起来,就可以是 字符数组。
1. 字符数组的初始化
char a[10]; //字符数组的创建
字符数组的创建同⼀维数组的创建就不再赘述,但是字符串数的初始化有2种⽅式,如下:
//式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'};
2. strlen求字符串⻓度
字符数组中存放的着字符串,这个字符数组有⾃⼰的⻓度,也就是数组的元素个数,这个可以使⽤sizeof 计算,那数组中存放的字符串的⻓度是多少?怎么来计算呢?其实C/C++中有⼀个库函数 叫: strlen ,可以求字符串的⻓度,其实统计的就是字符串中 \0 之前的字符个数。 strlen 需要 的头⽂件是<cstring> 。
size_t strlen ( const char * str );//str - 指针,存放的是字符串的起始地址,//从这个地址开始计算字符串的长度
eg
#include <iostream>
#include <cstring>using namespace std;int main()
{char arr[10] = "abcdef";//a b c d e f \ 0 0 0 0int n = strlen(arr);cout << n << endl;int sz = sizeof(arr) / sizeof(arr[0]);// 10 1cout << sz << endl;//输出 6 10
}
3. 字符数组的输⼊
3.1.输⼊没有空格字符串
3.11使⽤scanf函数和字符数组来实现:
其头文件#include <cstdio>
#include <iostream>
#include <cstdio>using namespace std;int main()
{char arr[10];//输入 --abcdefscanf("%s", arr);//输出printf("%s\n", arr);//---输出abcdef return 0;
}
3.12也可以使用cin和cout
但是他们只能使用于不带空格的字符串
#include <iostream>
#include <cstdio>using namespace std;int main()
{char arr[10];cin >> arr;cout << arr;//输入abcdef//输出abcdef return 0;
}
那如果输出带空格的字符串应该用什么呢?
scanf
#include <iostream>
#include <cstdio>using namespace std;int main()
{char arr[20];scanf("%s",arr);printf("%s\n",arr);//输入 abc def//输出 abc return 0;}
当输⼊"abcdef"的时候,实际上scanf只读取了abc就结束了,也就是相当于遇到空格就结束了。
这⾥特别说⼀下占位符 %s ,它其实不能简单地等同于字符串。它的规则是,从当前第⼀个 ⾮空⽩字符开始读起,直到遇到空⽩字符(即空格、换⾏符、制表符等)为⽌。 因为 %s 的读取不会包含空⽩字符,所以⽆法⽤来读取多个单词,除⾮多个 这也意味着, %s ⼀起使⽤。 scanf() 不适合读取可能包含空格的字符串,⽐如书名或歌曲名。另外有⼀ 个细节注意⼀下, scanf() 遇到 %s 占位符,会在字符串变量末尾存储⼀个 \0 字符。
同时 scanf() 将字符串读⼊字符数组时,不会检测字符串是否超过了数组⻓度。所以,储存字符串 时,很可能会超过数组的边界,导致预想不到的结果。为了防⽌这种情况,使⽤ %s 占位符时,可以 指定读⼊字符串的最⻓⻓度,即写成%[m]s,其中的 [m] 是⼀个整数,表⽰读取字符串的最⼤⻓度,后⾯的字符将被丢弃。
cin cout
#include <iostream>
#include <cstdio>using namespace std;int main()
{char arr[20];cin >> arr;cout << arr << endl;//输入abc def//输出abc return 0;}
结果也是⼀样,没有任何区别。
其实cin在读取⼀个字符串的时候,在遇到空⽩字符的时候,就认为字符串结束了,不再继续 往后读取剩余的字符,同时将已经读取到的字符串末尾加上\0,直接存储起来。
1.13.解决办法
1.14gets和fgets
char * gets ( char * str );char * fgets ( char * str, int num, FILE * stream );
eg
#include <iostream>
#include <cstdio>using namespace std;int main()
{char arr[20];gets(arr);cout << arr;//输入abc edf//输出abc edfreturn 0;}
gets是怎么运行工作的呢
gets 是从第⼀个字符开始读取,⼀直读取到 \n 停⽌,但是不会读取 \n ,也就是读取到的内容 中没有包含 \n ,但是会在读取到的内容后⾃动加上 \0 。
#include <iostream>
#include <cstdio>using namespace std;int main()
{char arr[10] = {0};fgets(arr, sizeof(arr), stdin);printf("%s\n", arr);return 0;
}
注:即使这里标的是20个数,但fgets最多读取19个数
fgets 也是从第⼀个字符开始读取,最多读取 num-1 个字符,最后⼀个位置留给 \0 ,如果num 的⻓度是远⼤于输⼊的字符串⻓度,就会⼀直读取到 \n 停⽌,并且会读取 \n ,将 \n 作 为读取到内容的⼀部分,同时在读取到的内容后⾃动加上 \0 。
注:
在DevC++中使⽤gets函数,确实没有报错,但是在其他的IDE上,⽐如:VS2022上直接报 错,不允许使⽤gets函数。 所以在代码中还是慎⽤gets函数。
当然C语⾔中使⽤ scanf 函数其实也能做到读取带有空格的字符串,只是不常⻅⽽已。⽅式就是 将 "%s" 改成 "%[^\n]s" ,其中在 % 和 s 之间加上了 [^\n] ,意思是⼀直读取,直到遇到\n ,这样即使遇到空格也就不会结束了。
#include <iostream>
#include <cstdio>using namespace std;int main()
{char arr[10] = "xxxxxxxx";scanf("%[^\n]s", arr);printf("%s\n", arr);//输入abc def//输出abc def return 0;}
getchar
使⽤ getchar 逐个字符的读取,也是可以读取⼀个字符串的。
#include <iostream>
#include <cstdio>using namespace std;int main()
{char arr[20];int ch = 0;int i = 0;while((ch = getchar()) != '\n'){arr[i] = ch;i++;}arr[i] = '\0';//必须有一个'\0',不然程序会崩 cout <<arr;//输入abc def//输出abc def return 0;}
如果不想在结尾输入'\0',也可以在开始时初始化数组
#include <iostream>
#include <cstdio>using namespace std;int main()
{char arr[20] ={0};int ch = 0;int i = 0;while((ch = getchar()) != '\n'){arr[i] = ch;i++;}cout <<arr;//输入abc def//输出abc def return 0;}
4. 字符数组的输出
其实前面已经使用得差不多了,总结以下
1. C语⾔中可以在 printf 函数中使⽤ %s 占位符的⽅式,打印字符数组中的字符串。
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{char a[] = "hello world";cout << a << endl;printf("%s\n", a);// 输出 hello world
// hello worldreturn 0;
}
2. C++中使⽤ cout ,可以直接打印字符数组中的字符串内容。
#include <iostream>
using namespace std;
int main()
{char a[] = "hello world";int i = 0;while (a[i] != '\0'){cout << a[i];i++;}cout << endl;//输出 hello world return 0;
}
3.也可以采⽤循环的⽅式逐个字符打印字符串的内容。
#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;//输出 hello world return 0;
}
如果字符数组里没有'\0',不能构成字符串,又该如何打印呢?
#include <iostream>
#include <cstring>
using namespace std;
int main()
{char arr[] = {'a','b','c','d','e','f'};int sz = sizeof(arr)/sizeof(arr[0]);int i = 0;for(i = 0;i < sz; i++){cout << arr[i];}return 0;
}
5. strcpy和strcat
5.1.strcpy
使⽤字符数组可以存放字符串,但是字符数组能否直接赋值呢?
比如
char arr1[] = "abcdef";
char arr2[20] = {0};
arr2 = arr1;//这样这节赋值可以吗?
就⾏整型数组中,我们说的过⼀样,这⾥也是不⾏的。那么如何将arr1中的字符串,拷⻉到arr2中呢? 其实C/C++中有⼀个库函数叫strcpy,可以完成。
char * strcpy ( char * destination, const char * source );//destination - 是目标空间的地址//source - 是源头空间的地址//需要的头文件 <cstring>
eg
#include <cstdio>
#include <cstring> using namespace std; int main()
{char arr1[] = "abcdef";char arr2[20] = {0};strcpy(arr2, arr1);printf("%s\n", arr2); //输出abcdef return 0;
}
5.2.strcat
有时候我们需要在⼀个字符的末尾再追加⼀个字符串,那字符数组能直接追加吗?
⽐如:
char arr1[20] = "hello ";
char arr2[] = "world";
arr1 += arr2;//这样也是不行的
那怎么办呢?C/C++中有⼀个库函数叫 strcat,可以完成
char * strcat ( char * destination, const char * source );//destination - 是目标空间的地址//source - 是源头空间的地址//需要的头文件<cstring>
eg
#include <cstdio>
#include <cstring> int main()
{char arr1[20] = "hello ";char arr2[] = "world";strcat(arr1, arr2);printf("%s\n", arr1);//输出hello world return 0;}
除了上⾯的两个字符串相关函数外,其实C/C++中还提供了⼀些其他的函数,有兴趣的同学可以拓展学 习:https://legacy.cplusplus.com/reference/cstring/