1,一维数组的创建和初始化
数组是一组相同类型元素的集合。
数组的创建:由数组类型 数组名[常量表达式];构成
代码1:
int arr1[10];
代码2:
int count = 10;
int arr2[count];
代码3:
char arr3[10];
float arr4[1];
double arr5[13+7];
数组的类型可以是任意类型,数组名可以自己取,一般用arr来表示,arr是数组英文(array)的简写;常量可以是任意值,当然我们也可以用常量表达式来写,如代码2。
注意:数组创建,在C99标准之前[]里一定要是常量表达式才可以,不能使用变量,C99标准之后支持了变长数组的概念。例如下面数组可以在C99标准下的编译器上编译。
int n = 0;
scanf("%d" &n);
int arr[n];
数组的初始化:
数组的初始化是指,在创建数组的同时给数组的内容一些合理的初始值。
int arr1[10]={1,2,3};//这个数组元素个数为10,前三个数被初始化1 2 3,剩余的没有给出的默认初始化为0,也就传入的数组是[1 2 3 0 0 0 0 0 0 0]
int arr2[ ]={1,2,3,4};//没有给出数组大小的,数组的元素个数,根据后面的初始化内容来确定,这个就相当于int arr2[4]={1,2,3,4}
char arr3[3]{'a',98,'c'};//98是数字b的ASCII码值,与arr4[ ]{'a','b','c'}效果相同;
char arr4[ ]{'a','b','c'};//相当于传入数组的是a b c ,3个元素
char arr5[ ]="abcdef";//相当于传入数组的是a b c d e f \0,7个元素。
数组长度不是字符长度,字符长度是指\0之前的元素个数,数组长度时传入参数的个数,arr4的数组长度是3,字符长度大于等于3,是一个不确定的值
整型(int)变量不完全初始化,会被默认为0,字符型(char)变量不完全初始化会被默认为\0。
一维数组的使用:
对于我们之前介绍的操作符 [ ],下标引用操作符,他就是用来访问数组的操作符。
比如有一个数组:
int main()
{
int arr[ ] ={1,2,3,4,5,6,7,8,9,10};//编号0 1 2 3 4 5 6 7 8 9 ,这个编号从0开始,这里的编号就是数组的下标。例如 arr[4]=5
}
这里我们需要介绍一个函数,sizeof表示计算字节的长度,一个int型占4个字节,char型占一个字节,short型占2~4个字节,long型占4~8个字节,longlong型占8个字节,float型占4个字节,double型占8个字节(每个字节占8个 bit比特位)。
所以我们可以用sizeof来计算数组的长度;sz=sizeof(arr)/sizeof(arr[0]),数组的总字节比上第一个数组的字节。
int sz=sizeof(arr)/sizeof(arr[0]);
输出数组内容(打印数组中的每个元素),可以采用for循环遍历的方法
#include
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz;i++)
{
printf("%d ",arr[i]); //使用for循环遍历,将每一项依次打印出来,当然也可以逆序打印。
}
return 0;
}
逆序打印(就是将每个数倒着往回打印 只需要改变for循环的遍历条件即可)
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = sz - 1; i >= 0;i--)
{
printf("%d ",arr[i]); //使用for循环遍历,将每一项依次打印出来,当然也可以逆序打印。
}
return 0;
}
对数组的内容进行赋值:可以采用for循环遍历的方式
//一维数组的赋值
#include
int main()
{
int arr[10] = { 0 };//初始化为0
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < 10;i++)//数组有十个元素
{
arr[i] = i;//使用for循环,对每一项赋值
}
for (i = 0; i < sz;i++)
{
printf("%d ", arr[i]); //使用for循环遍历,将每一项依次打印出来,当然也可以逆序打印。
}
return 0;
}
键盘输入赋值:
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
{
int arr[10] = { 0 };//初始化为0
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < 10;i++)//数组有十个元素
{
scanf("%d", &arr[i]);//使用for循环,对每一项赋值
}
for (i = 0; i < sz;i++)
{
printf("%d ", arr[i]); //使用for循环遍历,将每一项依次打印出来,当然也可以逆序打印。
}
return 0;
}
总结:
1.数组的使用是通过下标来访问的,下标是从0开始。
2.数组的大小可以通过计得到。
一维数组在内存中存储(数组元素的地址)
每个整型变量占四个字节:
每个字符占一个字节:
注意%p打印地址;有上述地址我们知道,随着数组下标的增长,元素的地址也有规律的递增,我们可以知道:数组在内存中是连续存放的。
(在数组中存储一个整型的数,开辟一个四个字节的存储单元,第一个存储单元的地址就是这个这个数的地址)
2,二维数组的创建和初始化
二维数组的创建:
与一维数组相同,有数组类型,数组名,和常量表达式构成。不过这列应该有两常量表达式。
int arr[3][4];
char arr[4][5];
double arr[2][6];
二维数组的初始化:二维数组初始化时可以省略行,不能省略列
int arr[3][4]={1,2,3,4};//表示有三行四列我们可以大致理解为 1 2 3 4这样存储。
0 0 0 0
0 0 0 0
没有被赋值的默认初始化为0;但实际上他们也是连续存储的不过编号应该是[0][0],[0][1],[0][2],[0][3],[1][0],[1][1],[1][2],[1][3],[2][0],[2][1],[2][2],[2][3]这样。
我们可以说arr[0]存储的是{1,2,3,4},就是将二维数组理解成一维数组的数组。
arr[1]存储的是{0,0,0,0}
arr[2]存储的是{0,0,0,0}
int arr[3][4]={{1,2},{4,5}};//这个可以理解为1 2 0 0
4 5 0 0
0 0 0 0
int arr[][4]={{2,3},{4,5}};//二维数组初始化时可以省略行,不能省略列,不然不知道什么时候换行如何排数组,而行数可以根据给出的数据确定。这个可以理解为2 3 0 0,
4 5 0 0
一个两行四列的数组。
二维数组的使用:
二位数组的使用也是通过下标的方式
输出二维数组内容(打印数组中的每个元素),可以采用for循环嵌套的方法
#include
//打印二维数组
int main()
{
int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };
int i = 0;
for (i = 0;i < 3;i++)
{
int j = 0;
for (j = 0;j < 4;j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
return 0;
}
二维数组的赋值
#include
//二维数组的赋值
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++)
{
arr[i][j]=i*4+j;
}
}
for (i = 0;i < 3;i++)
{
int j = 0;
for (j = 0;j < 4;j++)
{
printf("%-3d", arr[i][j]);//占三格左对齐
}
printf("\n");
}
return 0;
}
二维数组的赋值(直接输入)
#define _CRT_SECURE_NO_WARNINGS
#include
//二维数组的赋值
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]);
}
}
for (i = 0;i < 3;i++)
{
int j = 0;
for (j = 0;j < 4;j++)
{
printf("%-3d", arr[i][j]);//占三格左对齐
}
printf("\n");
}
return 0;
}
二维数组在内存中的存储:
二维数组在内存中的存储也是连续存储的。
3,数组越界
数组的下标是有范围限制的,规定是从0开始,如果有n个元素,最后一个元素的下标就是n-;所以数组下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组和发空间的访问。C语言本身不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的。所以程序员在写代码时,要自己做好越界检查。
因为数组可能会出现越界的情况,所以我们做for循环遍历数组时,可以用sizeof来求数组的总元素个数。
数组越界如下:
二维数组的行和列也会出现数组越界
4,数组作为函数的参数
往往我们在写代码代码的时候,会将数组作为函数传给参数,比如我们要实现冒泡排序(这里要讲算法思想)
冒泡排序的核心思想:相邻两个元素进行比较,一趟冒泡排序让一个数据来到他最终应该出现的位置上(比如升序,就是将最大的数放的最后面)
#include
//冒泡排序
void bubble_sort(int arr[], int sz)
{
int i = 0;
//趟数
for (i = 0;i < sz - 1;i++)
{
//一趟冒泡排序
int j = 0;
for (j = 0;j < sz - 1 - i;j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = tmp;
}
}
}
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
int i = 0;
for (i = 0;i < sz;i++) {
printf("%d ", arr[i]);
}
return 0;
}
数组名的本质是首元素的地址,在上面bubble_sort函数中,传入参数应该是地址,应该用指针变量接收,int arr[ ],看似是数组,本质是指针变量(在这里也可以写成int* arr),所以在bubble_sort函数中,不能计算出数组的长度,我们可以用传参的形式将sz传入。
但若是要计算字符串的长度可以在自定义函数中调用strlen函数,并且strlen函数的传入参数可以是数组(数组的本质就是首元素地址),也可以直接是地址就是说:strlen(arr)(arr是我们创建的char类型的数组),strlen(str)(str是我们的自定义函数中用一个char类型的指针变量来接受数组的地址,是一个地址(char *str)),strlen函数中本来就是接受一个数组首元素的地址,看什么时候识别到\0,来计算字符串的长度。
int my_strlen(char* str)
{
int count = 0;
while (*str != 0)
{
str++;
count++;
}
return count;
}
指针变量在64位机器上的地址:64bit位-8字节,指针变量的大小都是8字节。在32位机器上的地址:32bit为-4字节。在VS编译器中x64的环境下都占8个字节;在x86的环境下都站八个字节,所以如果在dubble_sort函数中计算sz=sizeof(arr)/sizeof(arr[0])是等于一的(都是指针类型)
数组名的本质是首元素地址,但是有两个例外
1.sizeof(数组名),这里数组名,表示整个数组,计算的是整个数组的大小,单位是字节。
2.&数组名,这里数组名表示的是整个数组,取出的是整个数组的地址。
(strlen(数组名),计算的是这个数组字符的个数,strlen是计算字符个数的函数需要使用头文件
数组首元素,与第二个元素之间差了一个整型,一个整型占4个字节,向内存中申请了4个存储单元,可以看出arr表示的就是首元素的地址,而&arr表示的则是arr这个数组总的地址,一共有十个整型变量,与整个数组+1,差了40个字节,都对arr取地址了,就不能再想当然的表示为首元素的地址。
sizeof是一个操作符,用来计算变量(类型)所占空间的大小,不关注内存中存放的具体内容;
strlen是一个库函数,是专门求字符串长度的,只能针对字符串,从参数给定的地址向后一直找\0,统计\0之前出现的字符的个数。
二维数组名的理解:
二维数组名本质上也表示首元素的地址,表示的是首行元素的地址,我们可以测试一下:
可见这是一个3行4列元素,一行有4个元素,占16个字节,所以二维数组表示的是首行元素的地址。
5,数组实例:
1.三子棋
2.扫雷
(我们将在后面章节单独讲解)