目录
一、数组的概念
1. 数组是一组相同类型元素的集合,存放在数组内的值被称为数组元素,数组分为一维数组和多维数组。
2. 多维数组是根据数组每个元素是几维数组划分的:二维数组的每个元素是一维数组,三维数组的每个元素是二维数组,其他的数组依此类推,多维数组中最常见的数组是二维数组。
3. 数组的元素个数不能为0,数组内的每个元素必须是相同类型的。
4. 数组的type有很多,如int、char、float、结构体等。后面所讲到的字符串数组,指针数组、函数指针数组和结构体数组也都只不过是不同类型的数组而已,它们均可创建出一维、二维、多维的形式。
5. 在创建数组时要想清楚,想要创建几维数组,数组内每个元素的类型是什么。
二、数组的类型
1. 数组的类型是除掉数组名剩余的部分,例如,int arr[10],数组arr的类型是int [10]。
三、一维数组
3.1 一维数组的创建
1. 一维数组的语法格式:type arr_name[常量值];
2. type表示一维数组中的每个元素是什么类型的,arr_name表示一维数组的名称,[常量值]表示一维数组的大小。
3. “定义可存放几个什么类型的一维数组”。
#include <stdio.h>
int main()
{
int arr1[10];//定义可存放10个整型的一维数组
char arr2[4];//定义可存放4个字符的一维数组
float arr3[6];//定义可存放6个单精度浮点型的一维数组
//...
return 0;
}
3.2 一维数组的初始化
1. 一维数组的初始化分为完全初始化和不完全初始化。
2. 完全初始化:一维数组有多大,就初始化几个元素。
3. 不完全初始化:初始化的元素个数小于一维数组的大小,剩余未初始化的元素会默认初始化为0。
4. 省略数组大小的初始化:初始化一维数组时,[ ]中的常量值也可以省略不写,此时初始化几个元素,一维数组就为多大。
#include <stdio.h>
int main()
{
int arr1[10] = { 1, 2, 3, 4, 5, 6 ,7, 8, 9,10 };//完全初始化
int arr2[10] = { 0 };//不完全初始化
int arr3[] = { 1, 2, 3, 4 };//省略数组大小的初始化,数组大小为4
return 0;
}
3.3 一维数组的访问
1. 数组元素的访问需要用到下标引用操作符[ ],数组元素的下标都是从0开始,最大为sz-1。
2. 访问时的要点:①知道需要的变量在哪个数组中②知道变量在该数组中的下标编号是多少
③连续输入或使用数组元素时创建循环变量利用 for 循环解决。
//一维数组的输入和输出
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;//循环变量
for (i = 0; i < sz; i++)
{
scanf("%d", &arr[i]);
printf("%d ", arr[i]);
}
return 0;
}
3.4 一维数组在内存中的存储
1. 一维数组在内存中连续存储,并且随着下标的增长地址由小到大变化。
//验证一维数组在内存中的存储方式
#include <stdio.h>
int main()
{
char arr[5] = { '0', '1', '2', '3', '4'};//字符数组,每个元素占一个字节
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;//循环变量
for (i = 0; i < sz; i++)
{
printf("arr[%d]=%p\n", i, &arr[i]);
}
return 0;
}
四、二维数组
4.1 二维数组的创建
1. 二维数组的语法格式:type arr_name[常量值1][常量值2];
2. type表示二维数组中的每个元素是什么类型的,arr_name表示二维数组的名称,[常量值1]表示二维数组中有几个一位数组(即几行),[常量值2]表示每个一维数组中有几个元素(即几列)。
#include <stdio.h>
int main()
{
int arr[3][5];//定义三行五列的整型二维数组
char arr2[4][6];//定义4行6列的字符二维数组
float arr3[2][4];//定义2行4列的浮点数二维数组
//...
return 0;
}
4.2 二维数组的初始化
1. 二维数组的初始化分为完全初始化和不完全初始化,二维数组中的每个一维数组的{}可打可不打,不打{ }时元素按照排满一行再换一行的顺序摆放,打{ }时可以控制每行存哪些元素。
2. 完全初始化:二维数组有几列几行,就初始化几列几行的元素。
3. 不完全初始化:初始化的元素个数小于数组大小(行*列),剩余未初始化的元素默认初始化为0。
4. 省略二维数组行标的初始化:初始化二维数组时,常量值1也可以省略不写,但常量值2必须写, 编译器会根据初始化内容判断二维数组有几行。
#include <stdio.h>
int main()
{
int arr1[3][4] = { { 1, 2, 3, 4 }, { 2, 3, 4, 5 }, { 3, 4, 5, 6 } };//完全初始化
int arr2[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//完全初始化
int arr2[3][4] = { { 1 }, { 2, 3 }, { 4, 6, 8 } };//不完全初始化
int arr3[3][4] = { 0, 1, 2 };//不完全初始化
int arr4[][4] = { 1, 2 };//数组为1行4列
int arr5[][4] = { { 1 }, { 3 }, { 2 , 5 } };//数组为3行4列
return 0;
}
4.3 二维数组的访问
1. 二维数组的访问也要用到下标引用操作符[ ],二维数组的行标和列标都是0开始,最大为sz-1。
2. 访问时的要点:① 知道需要的变量在哪个数组中 ② 知道变量在该数组中的行标和列标是多少 ③ 连续输入或使用数组元素时创建循环变量利用嵌套 for 循环解决。
//二维数组的输入和输出
#include <stdio.h>
int main()
{
int arr[3][4] = { 0 };
int i = 0;//循环变量
for (i = 0; i < 3; i++)
{
int j = 0;//循环变量
for (j = 0; j < 4; j++)
{
scanf("%d", &arr[i][j]);
printf("%d ", arr[i][j]);
}
//换行
printf("\n");
}
return 0;
}
4.4 二维数组在内存中的存储
1. 二维数组和一维数组一样,在内存中连续存放,随着下标的增长地址由小到大变化。
//验证二维数组在内存中的存储方式
#include <stdio.h>
int main()
{
char arr[2][3] = { { '1', '2', '3' }, { '2', '3', '4' }};//字符数组,每个元素占一个字节
int i = 0;//循环变量
for (i = 0; i < 2; i++)
{
int j = 0;//循环变量
for (j = 0; j < 3; j++)
{
printf("arr[%d][%d]=%p\n", i, j, &arr[i][j]);
}
}
return 0;
}
五、变长数组
1. 概念:C99之前,数组的大小只能是常量。C99之后,引入了变长数组的概念,可以使用变量来指定数组的大小。但VS默认不支持C99中的变长数组,使用gcc编译器的DevC++支持,oj刷题的网站一般也是支持的。
2. 变⻓数组的根本特征:数组⻓度只有运⾏时才能确定,所以变⻓数组不能初始化。
3. 好处:当在开发时,没有确定好数组的大小,可以利用变长数组,待程序运⾏时再为数组分配精确的⻓度。
4. 注意:变⻓数组的意思是数组的⼤⼩是可以使⽤变量来指定的,在程序运⾏的时候,根据变量的⼤⼩来指定数组的元素个数,⽽不是说数组的⼤⼩是可变的。数组的⼤⼩⼀旦确定就不能再变化了。
5. 使用变长数组时,数组大小n要在数组创建之前就要存值。
六、sizeof计算数组的大小
1. sizeof是专门计算变量或类型所占空间大小的一个关键字,计算结果的单位是字节,sizeof不会执行( )内的任何运算和函数调用。
2. sizeof(数组名)计算的是整个数组的大小,这里的数组名不再是数组首元素的地址。
3. 计算一维数组元素个数:int sz = sizeof(arr) / sizeof(arr[0]);利用整个数组的大小除以单个数组元素的大小,得到的就是数组大小,打印时占位符%d不行就用%zd。
4. 计算二维数组元素个数:int sz = sizeof(arr) / sizeof(arr[0][0]);
5. 使用的好处:方便修改和利用。
6. 好习惯:定义和初始化一维数组后,下面一行立即写上用sizeof计算出的一维数组元素个数。
温馨提示:
1.下面讲到的字符串数组、指针数组、函数指针数组和结构体数组讲解的例子都只会使用它们的一维数组,它们的多维数组并不常用,所以就没有举例。
2. 字符串数组、指针数组、函数指针数组和结构体数组的一维数组和二维数组的创建、初始化、使用、在内存中的存储都差不多,只需要记住它们与内置类型差异点即可。
七、字符串数组
7.1 字符串数组的创建
1. C语言中没有专门的字符串类型,如果要存放字符串可以用字符串数组存放(字符串数组本质是字符数组)。
2. 字符串数组的语法格式(和一维字符数组、二维字符数组一样):
一位字符串数组:char arr_name[常量值];
二维字符串数组:char arr_name[常量值1][常量值2];
7.2 字符串数组的初始化
1. 字符串数组的初始化分为完全初始化和不完全初始化,且均有两种写法,第一种是用单引号初始化,在字符串的末尾会自动存入\0,这种写法在定义字符串数组的大小时要考虑\0也是数组中的一个元素,第二种是用大括号的初始化,但不会自动存入\0,需要手动存入\0才能代表这是一个字符串数组,否则只能作为一个字符数组。(通常会用双引号的形式初始化)
2. 下面代码中仅展示一维字符串数组初始化形式,因为二维字符串数组很少用到。
#include <stdio.h>
int main()
{
//一维字符串
//方法一:用双引号初始化
char arr1[4] = "abc";//完全初始化 a b c \0
char arr2[4] = "ab";//不完全初始化 a b \0
char arr3[] = "ab"//字符串数组大小为3
//方法二:用大括号的初始化
char arr4[4] = { 'a', 'b', 'c', '\0' };//完全初始化
char arr5[4] = { 'a', 'b', '\0'};//不完全初始化
char arr6[] = { 'a', 'b', '\0'};//字符串数组大小为3
char arr7[4] = { 'a', 'b', 'c', 'd' };//不是字符串数组,因为数组中没有\0
char arr8[4] = { 'a', 'b'};//不是字符串数组,因为数组中没有\0
char arr9[] = { 'a', 'b'};//不是字符串数组,因为数组中没有\0
return 0;
}
7.3 字符串数组的输入
1. 字符串数组的输入可以用scanf() 和 gets() 但两者是有区别的。
2. scanf() 读取字符串时以空格为分隔,遇到空格就认为当前字符串结束了,所以无法读取含有空格的字符串。
3. gets() 认为空格也是字符串的一部分,只有遇到回车键时才认为字符串输入结束,所以在字符串数组输入时,最好用 gets() 。
#include <stdio.h>
int main()
{
char arr1[10] = { '0'};
//输入方法一:
scanf("%s", arr1);//"hello world",只会读取hello
//输入方法二:
gets(arr1);//"hello world",全部读取
return 0;
}
7.4 字符串数组的输出
1. 字符串数组的输出可以用用scanf() 和 gets() 但是有区别的。
2. printf():通过格式控制符%s 输出字符串,不能自动换行。
3. puts():输出字符串并自动换行,该函数只能输出字符串。
#include <stdio.h>
int main()
{
char arr1[10] = "abcde";
//输出方法一:
printf("%s\n", arr1);//不会自动换行,打印时加\n
//输出方法二:
puts(arr1);//会自动换行
return 0;
}
7.5 字符串数组经典应用
//【实现:多个字符从两端移动,向中间汇聚】
//##########################
//Y########################!
//Yo######################!!
//You####################!!!
//You ##################!!!!
// ...
//You are the best!!!!!!!!!!
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
int main()
{
char arr1[] = "You are the best!!!!!!!!!!";
char arr2[] = "##########################";
//计算数组arr2的元素个数
int sz = sizeof(arr2) / sizeof(arr2[0]);
//再屏幕上先打印数组arr2的内容
printf("%s\n", arr2);
int left = 0;
int right = sz - 2;//字符串的末尾隐藏着一个\0
while (left <= right)
{
//睡眠1000毫秒
Sleep(700);//sleep的第一个字母大写!
arr2[left] = arr1[left];
arr2[right] = arr1[right];
//清屏
system("cls");//system函数是库函数,执行系统命令,cls清理控制台屏幕
//再次在屏幕上打印数组arr2
printf("%s\n", arr2);
//调整
left++;
right--;
}
return 0;
}
八、指针数组
8.1 指针数组
1. 指针数组就是存放指针的数组,数组中的每个元素都是指针(地址)。
2. 指针数组的类型由数组中每个元素解引用所指向变量的类型决定。
#include <stdio.h>
int main()
{
int arr1[] = { 1, 2, 3 };
int arr2[] = { 2, 3, 4 };
int arr3[] = { 3, 4, 5 };
int* arr[3] = { arr1, arr2, arr3 };//一维指针数组
//由于数组名是数组首元素的地址,每个数组首元素的地址是int*类型的
//所以arr指针数组语法中的tpye部分要写成int*
return 0;
}
8.2 指针数组模拟实现二维数组
1. 模拟的⼆维数组,并⾮完全是⼆维数组,因为每⼀⾏的数据在内存中是⾮是连续的。
2. 下图代码的解释:parr是数组首元素的地址,而parr首元素的地址是数组arr1的地址,我们可以通过对parr+-整数拿到parr中每个元素的地址,即内部每个数组的地址,再通过对这些地址解引用拿到,每个内部数组首元素地址,最后通过首元素的地址偏移和解引用,找到parr内部数组中的每个元素,即有了 *(*(arr+i) + j) == parr[i][j] 。
#include <stdio.h>
int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[] = {2,3,4,5,6};
int arr3[] = {3,4,5,6,7};
int* parr[3] = {arr1, arr2, arr3};//一位指针数组
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
九、函数指针数组
9.1 函数指针数组
1. 函数指针数组是存放函数指针的数组,函数指针数组里的元素全都是函数指针。
2. 函数指针数组的类型由数组中每个元素的类型决定,但函数指针数组的创建写法比较独特,具体见下方代码。
即要把函数指针类型中写指针名称的地方写成函数指针数组名+数组大小。
#include <stdio.h>
int Add(int x, int y)
{
//...
return x + y;
}
int Sub(int x, int y)
{
//...
return x - y;
}
int Mul(int x, int y)
{
//...
return x * y;
}
int main()
{
int (*arr[3])(int, int) = { Add, Sub, Mul };//函数指针数组
//arr是数组名,[3]表示数组大小
//int ret = (arr+1)(2, 3);//err
//int ret = (*(arr + 1))(2, 3);
int ret = arr[1](2, 3);//下标直接访问
printf("%d\n", ret);
return 0;
}
9.2 转移表
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
do
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf(" 0:exit \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
if ((input <= 4 && input >= 1))
{
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = p[input](x, y);
printf("ret = %d\n", ret);
}
else if (input == 0)
{
printf("退出计算器\n");
}
else
{
printf("输⼊有误\n");
}
} while (input);
return 0;
}
十、结构体数组
1. 结构体数组是存放同一种结构体类型变量的数组,结构体数组里的元素全都是同一个结构体类型创建的结构体变量。
2. 结构体数组的类型由数组中每个元素是什么结构体类型决定。
//首先声明结构体类型
struct stu
{
char name[20];
int age;
};
//定义结构体数组,并初始化
struct stu mate[3]={{"Allen",18},{"Smith",19},{"Grace",18}};
十一、图片
本篇文章已完结,谢谢支持哟 ^^ !!!