数组
1、一维数组的创建和初始化
2、一维数组的使用
3、一维数组在内存中的存储
4、二维数组的创建和初始化
5、二维数组的使用
6、二维数组在内存中的存储
7、数组越界
8、数组作为函数参数
1、一维数组的创建和初始化
1.1 数组的创建
数组是一组相同类型元素的集合。
数组的创建方式:
type_t arr_name [const_n]
//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小
实例1:
这些都是可以的。
但是下面这样不行,下面这个代码之能在支持C99标准的编译器上。
且 int arr1[n];//这种数组是不不能初始化的。
1.2 数组的初始化
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)
发现但ch1和ch2 存的一样,但ch1和ch2 的性质是不一样的
ch1初始化我是自己主动放进去3个,但ch2我是主动放进去4个,还有个\0
虽然放进去的结果一样。
这几种也是:
分析 :
arr2数组初始化就是3个元素,这个arr2数组就是3个元素
ch4是拿字符串来初始化的,字符串里面默认含有\0这个元素
总结:
int arr1[10]={1,2,3};
int arr2[ ]={1,2,3};
int arr3[10]={1,2,3,4,5,6,7,8,9,0};
int arr4[3]={'a',98,'c'};
int arr5[ ]={'a','b','c'};
int arr6[ ]="abcdef";
1.3 一维数组的使用
操作符:[ ] ,下标引用操作符。就是数组访问的操作符。
就放一个数组,但我也不知道它有几个元素。
当写arr[4]的时候,其实找的是下标为4的元素,把他打印出来
把这个元素全部打印出来
倒着打印出来
总结:
1、数组是使用下标来访问的,下标从0开始。
2、数组的大小是可以通过计算的到的。
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
1.4 一维数组在内存中的存储
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
//打印数组的每个元素的地址
for (i = 0; i < sz; i++)
{
printf("&arr[%d] = %p\n",i, &arr[i]);//%p是用来打印地址的,那就把地址取出来,用&
}
return 0;
}
结果:
这个地址是十六进制的。
发现每个元素直接差的都是4,那为什么是4呢,因为一个整型元素站4个字节,那第一个元素和第二个元素的地址就是差4,一个字节给一个地址编号, 一个字节给一个地址编号,
那此时打开调试看下内存:
所以这个地址相差的是4,这个地址一直相差4,也印证了这个数组在内存中是连续存放的。
随着数组下标的增长。元素的地址也在有规律的递增。
2、二维数组的创建和初始化
2.1 二维数组的创建
//数组创建
int arr[3][4];
char arr[3][5];
double arr[2][4];
2.2 二维数组的初始化
//数组初始化
int arr[3][4]={1,2,3,4};
int arr[3][4]={{1,2},{3,4}};
int arr[ ][4]={{2,3},{4,5}}; //二维数组如果初始化,行可以省略,列不能省略。
因为前面已经限定了一行放四个元素,所以第一行放四个元素之后紧接着后面初始化内容又找四个给第二行,再找四个放第三行。
那如果此时给的不够呢,少几个呢?其实就是先来四个放第一行,再来四个放第二行,剩下的不够给第三行就补0 0.
那次是我希望把几个数据放在我想放的行数上。比如这个数据 int arr1[3][4]={1,2,3,4,5,6};
1 2放第一行,3 4放第二行 ,5 6放第三行 ,这个时候就需要做一个小的分组。
二维数组如果初始化,行可以省略,列不能省略。
当然,也可以不分组,直接初始化,一行先放四个,剩下的再放第二行、第三行,如果不够就不放了。
三维数组也是只能省略第一维,第二维和第三维也是不能省的。
2.3 二维数组的使用
二维数组的使用也是通过下标的方式。
#include<stdio.h>
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<stdio.h>
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++)
{
scanf("%d", &arr[i][j]);
}
}
for (i = 0; i < 3; i++)
{
int j = 0;//列
for (j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}//打印完一行
printf("\n");
}
return 0;
}
结果:
可以把二维数组理解为:一维数组的数组。
把一行看作一个元素,第一行是一个元素,第二行是一个元素,第三行是一个元素,有三个元素.
每个元素是一维数组。
2.4 二维数组在内存中的存储
#include<stdio.h>
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("&arr [%d][%d] = %p\n",i,j, &arr[i][j]);
}
}
return 0;
}
结果:
二维数组在内存中也是连续存放
3、 数组越界
数组的下标是有范围限制的。
数组的下表规定是从0开始的,如果数组有n个元素,最后一个元素下标就是n-1.
所以数组下标如果小于1,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
C语言本身是不做数组下标的越界检查的,编译器也不一定会报错,但是!编译器不报错并不意味着程序本身就是正确的。
所以写代码时,最好自己做越界的检查。
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6 };//下标0-5
int i = 0;
for (i = 0; i < 10; i++)//这里明显越界了
{
printf("%d ", arr[i]);
}
return 0;
}
结果:
没报错,但是会有警告。
二维数组的行和列也可能存在越界。
4、数组作为函数参数
将数组作为参数传给函数,比如:我要实现一个冒泡排序函数。
将一个整型数组排序。
4.1冒泡排序
一套冒泡排序,让一个数据来到它最终应该出现的位置。
那这个时候十个元素需要多少套冒牌排序。需要9套。因为我们9个已经交换到最终应该出现的位置,那剩下的一个元素也已经在最终应该出现的位置。
如果是n个元素,那我就要进行n-1趟冒泡排序。而一趟内部就是两个相邻元素比较。
错误示例:
#include<stdio.h>
//数组传参的时候,形参有两种写法:
//1、指针
//2、数组
//形参是数组的形式
void bubble_sort(int arr[])
{
//确定趟数
int sz = sizeof(arr) / sizeof(arr[0]);
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 temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main()
{
//数组
//把数组的数据排成升序
int arr[] ={9,8,7,6,5,4,3,2,1,0};
//这个数组最后结果放的是0 1 2 3 4 5 6 7 8 9
int sz = sizeof(arr) / sizeof(arr[0]);
//冒泡排序的算法,对数组进行排序
bubble_sort(arr);//数组在传参的时候直接给个数组名就可以了
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
结果:
发现不对,打开调试界面发现,函数内部的sz本应该是10的,但这里显示是2(64位是2,32位是1)
发现这个循环就进去了一次,就排了一次序,所以发现打印出来的内容就只交换了一次。
分析:再次强调一遍
正确代码: 第一种形参是数组的形式
#include<stdio.h>
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 temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main()
{
//数组
//把数组的数据排成升序
int arr[] ={9,8,7,6,5,4,3,2,1,0};
//这个数组最后结果放的是0 1 2 3 4 5 6 7 8 9
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;
}
结果:
4.2数组名是什么?
前面说数组名是首元素地址,这句话其实是有一点不准确的。
接下来纠正一下,通过代码来具体看看:
#include<stdio.h>
int main()
{
int arr[10];
printf("%p\n", arr);//打印这个数组的地址
printf("%p\n", &arr[0]);//首元素地址
return 0;
}
结果:
发现这两个地址一摸一样,数组名确实就是首元素地址。
那这个时候看这条语句 ,int n = sizeof(arr); ,数组名其实就是首元素的地址的话,地址是8个字节或4个字节,那也就是说n=4,或者n=8吗?打印看看:
发现打印的结果是40,这就跟前面矛盾了。
纠正:
数组名确实能表示首元素的地址,
但是有两个例外:
1、sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
整个数组是40个字节,10个元素,每个元素整型4个字节,所以这是40;
2、&数组名 , 这里的数组名,也是表示整个数组,取出的是整个数组的地址。
#include<stdio.h>
int main()
{
int arr[10];
printf("%p\n", arr);//打印这个数组的地址
printf("%p\n", &arr[0]);//首元素地址
printf("%p\n", &arr);
//int n = sizeof(arr);
//printf("%d ", n);
return 0;
}
分析:
结果:
发现结果一摸一样。
分析:
从值的角度来说,这三个值,值是一摸一样的,因为数组的地址从那开始,数组的首元素地址也是从那开始,所以printf("%p\n", arr);和printf("%p\n", &arr[0])这两个一样是没有问题的。而这个printf("%p\n", &arr);取地址数组名去取出的也是这个数组的地址,是整个数组的地址,跟前面两个好像一样,那这两类有什么区别? 给他们分别加个1看下:
所以,数组的地址和数组的首元素地址还是有区别的。所以这三个地址虽然是一样,但意义不一样。
总结:
两个例外:1、sizeof(数组名) 2、&数组名
以后遇到的所有数组名,除了这两种之外,都表示首元素地址。
冒泡排序 (传数组,指针接收)
因为我传的是首元素地址,这里数组名表示首元素地址,9的地址,是个整型的地址,用整型指针接收。
第二种:形参是指针的形式
#include<stdio.h>
形参是指针的形式
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 temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main()
{
//数组
//把数组的数据排成升序
int arr[] ={9,8,7,6,5,4,3,2,1,0};
//这个数组最后结果放的是0 1 2 3 4 5 6 7 8 9
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;
}
刚刚讲的都是一维数组,那二维数组数组名应该怎么来理解。
4.3二维数组数组名的理解
3*4=12,12个元素,整型,12*4=48. 所以对二维数组跟一维数组的理解是一样的。数组名就是这样子。那&数组名呢。依然取出得是整个二维数组的地址。
那直接写个 arr; 是什么。
二维数组的数组名也表示首元素的地址,那他表示的是谁的地址。
这个地方+1,跳过了16个字节。
所以二维数组的数组名表示一行的地址。
那我想通过计算得到它有几行,几列。
行数=总数组大小/一行大小
列数=一行的大小/一个元素的大小
#include<stdio.h>
int main()
{
int arr[3][4] = {0};
sizeof(arr);//总数组大小
sizeof(arr[0]);//第一行的数组名arr[0]单独放到sizeof内部,计算第一行的大小
sizeof(arr[0][0]);//元素大小
printf("行数=%d\n", sizeof(arr) / sizeof(arr[0]));//行数
printf("列数=%d\n", sizeof(arr[0]) / sizeof(arr[0][0]));//行数
return 0;
}
结果:
分析: