前言
从这篇文章开始,我们就要进入C语言的重要模块——数组的学习,本文包括一维数组、二维数组的概念、使用以及介绍一下变长数组,最后会有超有价值的小练习,可根据目录跳转自己需要的内容。
数组
1. 数组的概念
数组是一组相同类型元素的集合。从该概念可得两个关键信息:
- 数组中存放的是1个或多个数据,数组元素个数不能为0。
- 数组中存放的多个数据,类型相同。
数组分为一维数组和多维数组,常见的多维数组是二维数组。
2. 一维数组的创建和初始化
2.1 数组创建
一维数组创建的基本语法:
type arr_name[常量值];
type
指定数组中存放数据的类型,如char
、short
、int
、float
等,也可以是自定义类型。arr_name
是数组名,应取有意义的名字。[]
中的常量值用于指定数组的大小,根据实际需求确定。
例如:
int math[20]; ://用于存储20人的数学成绩。
char ch[8];
double score[10];
2.2 数组的初始化
数组创建时给定初始值,称为初始化。数组初始化一般使用大括号将数据括起来。
//**完全初始化**:`
int arr[5] = {1,2,3,4,5};
//**不完全初始化**:
int arr2[6] = {1};//第一个元素初始化为1,剩余元素默认初始化为0。
//**错误的初始化 - 初始化项太多**:`
int arr3[3] = {1, 2, 3, 4};//,初始化项数量超过数组大小。
2.3 数组的类型
数组是一种自定义类型,去掉数组名留下的就是数组的类型。
int arr1[10];
,arr1
数组的类型是int [10]
。int arr2[12];
,arr2
数组的类型是int [12]
。char ch[5];
,ch
数组的类型是char [5]
。
3. 一维数组的使用
3.1 数组下标
C语言规定数组有下标,且从0开始。假设数组有n
个元素,最后一个元素的下标是n - 1
,下标相当于数组元素的编号。也叫数组的索引。
数组访问使用[]
下标引用操作符。例如:
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n", arr[7]);//8
printf("%d\n", arr[3]);//4
return 0;
}
输出结果:
8
4
3.2 数组元素的打印
要访问整个数组内容,可使用for
循环产生数组所有元素的下标,再通过下标访问。这个过程也称为遍历数组。
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int i = 0;
for(i=0; i<10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
输出结果:
3.3 数组的输入
根据需求给数组输入数据:
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int i = 0;
for(i=0; i<10; i++)
{
scanf("%d", &arr[i]);
}
for(i=0; i<10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
4. 一维数组在内存中的存储
通过打印数组元素的地址了解其存储方式:
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int i = 0;
for(i=0; i<10; i++)
{
printf("&arr[%d] = %p\n ", i, &arr[i]);//%p元素的地址
}
return 0;
}
运行结果是十六进制数,数组随着下标的增长,地址由小到大变化,且相邻元素地址相差4字节(因一个整型是4个字节)。结论:数组在内存中是连续存放的,为后期使用指针访问数组奠定基础。
5. sizeof计算数组元素个数
sizeof
是C语言关键字,可计算类型或变量大小,也能计算数组大小。
例如:
#include <stdio.h>
int main()
{
int arr[10] = {0};
printf("%d\n", sizeof(arr));
return 0;
}
输出结果是40,计算的是数组所占内存空间的总大小,单位是字节。
计算数组元素个数:
#include <stdio.h>
int main()
{
int arr[10] = {0};
int sz = sizeof(arr)/sizeof(arr[0]);
printf("%d\n", sz);
return 0;
}
结果是10,表示数组有10个元素。以后在代码中需要数组元素个数的地方,可使用此计算方式,数组变化时,计算出的大小也会相应变化。
6. 二维数组的创建
6.1 二维数组的概念
把一维数组作为数组元素,就是二维数组。将二维数组作为数组元素的数组称为三维数组,二维数组以上的数组统称为多维数组。
6.2 二维数组的创建
定义二维数组的语法:
type arr_name[常量值1][常量值2];
例如:
int arr[3][5];//表示数组有3行,每行有5个元素,每个元素是整型。
double data[2][8];
7. 二维数组的初始化
在创建变量或数组时给定初始值,称为初始化。二维数组使用大括号初始化。
7.1 不完全初始化
int arr1[3][5] = {1,2};
int arr2[3][5] = {0};
7.2 完全初始化
int arr3[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
7.3 按照行初始化
int arr4[3][5] = {{1,2},{3,4},{5,6}};
7.4 初始化时省略行,但不能省略列
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}};
8. 二维数组的使用
8.1 二维数组的下标
二维数组访问使用下标形式,行和列都从0开始。锁定行和列就能唯一锁定数组中的一个元素。
图中最右侧绿⾊的数字表⽰⾏号,第⼀⾏蓝⾊的数字表⽰列号,都是从0开始的,⽐如,我们说:第1
⾏,第4列,快速就能定位出6。
例如:
#include <stdio.h>
int main()
{
int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
printf("%d\n", arr[1][4]);
return 0;
}
输出结果:
8.2 二维数组的输入和输出
访问整个二维数组,可借助循环生成所有的行和列下标。
#include <stdio.h>
int main()
{
int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
int i = 0;//遍历行
//输入
for(i=0; i<3; i++) //产生行号
{
int j = 0;
for(j=0; j<5; j++) //产生列号
{
scanf("%d", &arr[i][j]); //输入数据
}
}
//输出
for(i=0; i<3; i++) //产生行号
{
int j = 0;
for(j=0; j<5; j++) //产生列号
{
printf("%d ", arr[i][j]); //输出数据
}
printf("\n");
}
return 0;
}
9. 二维数组在内存中的存储
通过打印数组所有元素的地址研究其存储方式:
#include <stdio.h>
int main()
{
int arr[3][5] = { 0 };
int i = 0;
for (i = 0; i < 3; i++) {
int j = 0;
for (j = 0; j < 5; j++)
{
printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
}
}
return 0;
}
输出结果显示,每一行内部的每个元素都是相邻的,地址相差4个字节,跨行位置的两个元素(如arr[0][4]
和arr[1][0]
)之间也相差4个字节。所以二维数组中的每个元素都是连续存放的,了解其内存布局有利于后期使用指针访问数组的学习。
10. C99中的变长数组
在C99标准之前,C语言创建数组时,数组大小只能使用常量、常量表达式,或者初始化数据时省略数组大小。
例如:
int arr1[10];
int arr2[3+5];
int arr3[] = {1,2,3};
这种语法限制导致创建数组不够灵活。C99引入变长数组(VLA)特性,允许使用变量指定数组大小。
例如:
int n = a+b;
int arr[n];
数组arr
是变长数组,其长度取决于变量n
的值,编译器无法事先确定,只有运行时才能知道。变长数组不能初始化,好处是程序员无需在开发时随意指定数组估计长度,程序可在运行时为数组分配精确长度。需要注意,变长数组的大小在运行时根据变量确定,但数组大小一旦确定就不能再变化。
在VS2022上不支持C99中的变长数组,无法测试。在gcc编译器(比如Dev c++)上测试示例:
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);//根据输入数值确定数组的大小
int arr[n];
int i = 0;
for (i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
for (i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
测试结果:
那么,我在VS2022中也想使用变长数组,该怎么办呢?
1 调整VS2022的配置,可以自行寻找教程,改用gcc编译器(不建议,新手不要去做,浪费时间,没有意义)
2 使用动态分布也能达到同样效果,这部分内容敬请期待,会单独写文章。
11. 数组练习
练习1:多个字符从两端移动,向中间汇聚
编写代码,演⽰多个字符从两端移动,向中间汇聚
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "welcome to C world...";
char arr2[] = "#####################";
int left = 0;
int right = strlen(arr1) - 1;
printf("%s\n", arr2);
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
left++;
right--;
printf("%s\n", arr2);
}
return 0;
}
这样基本要求就达到了,但我们不妨完善一下,有没有办法体现过程的感觉呢?有的兄弟,有的,这样的方法一共有 ,我们使用一个函数Sleep(),原型和头文件为,括号里填时间,单位为毫秒,可以前线程暂停执行一段时间,注意:不同编译器Sleep的首字母大小写不同,VS2022为大写
#include <windows.h>
void Sleep(DWORD dwMilliseconds);
比如
Sleep(1000);//1s
但是这么打印出怎么一堆也不好看,有没有一个一个出现的办法呢?这里要用到一个代码
system("cls");//清理屏幕
注意:system
需要一个头文件#include<stdlib.h>
最终代码为
#include<stdio.h>
#include<string.h>
#include<Windows.h>
#include<stdlib.h>
int main()
{
char arr1[] = "welcome to C world";
char arr2[] = "##################";
int left = 0;
int right = strlen(arr1) - 1;//strlen不包括‘\0’
while(left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000);//单位毫秒,看到这个过程
system("cls");//清理屏幕
left++;
right--;
}
printf("%s\n", arr1);
return 0;
}
结果是动态的,这里不演示了,自己试试吧
练习2:二分查找
查找最简单的想法是挨个找,最直接了当,代码为
#include<stdio.h>
int main()
{
int arr[] = { 9,5,6,7,8,4,2,1,3,0 };
int n = 0;
scanf("%d", &n);
//1挨个找
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int flag = 0;//0没找到
for (i = 0; i < sz; i++)
{
if (arr[i] == n)
{
printf("找到了,下标是:%d\n", i);
flag = 1; //找到了
break;
}
}
if (flag == 0)
printf("找不到\n");
return 0;
}
在升序数组中查找指定数字,二分查找(折半查找)效率更高。
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int n = 0;
scanf("%d", &n);
//折半查找
int sz = sizeof(arr) / sizeof(arr[0]);
int left = 0;
int right = sz - 1;
int flag = 0;//找不到
while(left <=right)
{
int mid = (left + right) / 2;//必须在循环里面
if (arr[mid] < n)
{
left = mid + 1;
}
else if (arr[mid] > n)
{
right = mid - 1;
}
else
{
printf("找到了,下标是:%d\n", mid);
flag = 1;
break;
}
}
if (flag == 0)
printf("找不到\n");
return 0;
}
求中间元素的下标,使⽤ mid = (left+right)/2
,如果left和right⽐较⼤的时候可能存在问题,可以使⽤下⾯的⽅式:
mid = left+(right-left)/2;
(关于查找与排序,后面也会出其他文章详细讲解,敬请期待)
总结
本篇文章主要介绍数组的相关内容,内容需要认真记忆练习,文末的两个小练习需要稍加训练和理解,好了,本篇文章就到此结束,愿我们一直坚持努力,实现自己目标,希望三连支持一下,谢谢。