文章目录
- 1. 数组名的理解
- 2. 使用指针访问数组
- 3. 一维数组传参的本质
- 4. 冒泡排序
- 5. 二级指针
- 6. 指针数组
- 7. 指针数组模拟二维数组
hello,大家好呀,窝是脆皮炸鸡。这一期是关于数组和指针的,我觉得并不是很难,但是我觉着下一期可能稍难,大家有什么不懂的可以在评论区询问,加油,开始学习叭!!!
Ps:点赞收藏加关注,追番永远不迷路。
1. 数组名的理解
大家是否记得在上一期的指针内容里,我曾说过:数组名是数组首元素的地址。但是有两个例外,这两个例外是:sizeof(数组名),&数组名。除了这两个情况,其余情况下,数组名就是数组首元素的地址。
----------------------------------------------- 1 -----------------------------------------------
举个例子:sizeof(数组名)
int arr[7] = { 0 };
printf("sizeof(arr)=%d\n", sizeof(arr)); //这里sizeof是在计算arr的大小
大家知道此处打印的结果是什么吗,是28。如果按之前说的”数组名都是数组首元素的地址“,那首元素的地址占四个字节,为什么输出是28呢?
因为,此处数组名代表的并不是数组首元素地址。sizeof(数组名),其中的数组名表示的是整个数组,计算的是整个数组的大小,单位是字节。我们再回头看,这个数组共7个整数,一个整数占4个字节,所以一共占4*7=28个字节
----------------------------------------------- 2 -----------------------------------------------
举个例子:&数组名
int arr1[7] = { 0 };
printf("arr1 =%d\n", arr1); //这里的数组名在两种情况外,是数组首元素地址
printf("&arr1 =%d\n", &arr1); //整个数组的地址
事实上,打印出来的两个结果是相同的,都是数组首元素地址。
这是合乎情理的,arr1(不是那两种特殊情况)代表的是数组首元素地址;&arr1代表的是数组的地址。从下图可以看出,两者的起始地址一样。
在此处我们看不出它俩的差异。
大家是否记得之前的一句话“指针类型决定了指针的差异”。整型指针+1,跳过4个字节;字符指针+1,跳过1个字节。那数组指针+1,应当是跳过整个数组的。
printf("arr1 =%d\n", arr1); //数组名
printf("arr1+1 =%d\n", arr1+1);
printf("&arr1 =%d\n", &arr1); //整个数组的地址
printf("&arr1+1 =%d\n", &arr1+1);
(最后两个:44到48,+4;44到72,+28)图上显示:数组首元素地址+1,跳过4个字节;数组地址+1,跳过28个字节,即跳过整个数组。
总结:&数组名:这里的数组名表示整个数组,取出的是整个数组的地址。
2. 使用指针访问数组
在此强调一下:
- 数组是数组(是一块连续的空间,可存放一个或多个数组)
- 指针是指针(用于存放地址的变量)
- 它俩不是一回事,但是可以使用指针来访问地址。
为什么可以用指针来访问数组?
1.数组在内存中是连续存放的(指针+1是很方便遍历数组的)
2.指针的元素很方便遍历数组,取出数组内容。
我注释掉的方法是以前学习的方法,没有注释的方法是使用指针来访问的
int main()
{
int arr[6] = { 0 };
//输入
int index = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (index = 0; index < sz; index++)
{
/*scanf("%d", &arr[index]); */
scanf("%d", arr + index);
//arr是首元素地址,+1,跳过(4个字节)一个整数。
//arr+index,第一次循环就是arr+0,即首元素地址;第二次循环,arr+1,即下一个整数
}
//输出
for (index = 0; index < sz; index++)
{
/*printf("%d ", arr[index]); */
printf("%d ", *(arr + index));
//将每个元素的地址解引用,即可找到地址所指向的对象
}
return 0;
}
将输出的两种方式对比,arr[index] 和 * (arr+index) 它俩是完全等价滴。【其实,编译器在看到arr[index]是转化成 *(arr+index)来看的】
3. 一维数组传参的本质
数组是可以通过实参传递给函数的,现在我们来讨论一下数组传参的本质。大家先思考一下,假设我们要将数组int arr[7]传过去,当时我们说过,数组传的是数组名(实参),形参用什么来接收嘞?int arr [ ](数组大小可忽略)
第一种我们直接计算,结果是正确的;但在函数里却错了,为什么呢?
我们仔细看一下,print(arr),不是那两种例外情况,所以arr是数组首元素地址,要是地址的话,我们应该用int* arr接收的,然后(首元素地址4个字节) / (首元素地址4个字节)=1。到这我们就找到问题了。【之前我们之所以用int arr[ ]接收,只是为了方便理解,当时还没有学指针。】数组传参的时候,形参可以写成数组(易理解,但其实本质还是指针),也可以写指针变量。
总结:从中可以看出,数组传参的本质,传递的是首元素的地址,即便我们将形参写成数组的形式,它本质上也是一个指针变量。
那如果我们就是想将数组传过去呢?这时,我们不仅需要将数组名传过去,还需要将数组元素个数sz传过去,接下来举例:先将数组传给函数,再通过函数打印数组。
void print(int* arr, int sz)
{
int index = 0;
for (index = 0; index < sz; index++)
{
printf("%d ", *(arr + index));
}
}
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
print(arr, sz); 将数组大小也传过去
return 0;
}
4. 冒泡排序
冒泡排序的核心思想就是:两两相邻的元素进行比较。不满足顺序就交换,满足顺序就比较下一对。
以 “将乱序的数字变为有序的数字(升序/降序) ” 为例进行分析。(我这里是升序)
- 首先输入数列元素。
- 然后冒泡排序,确定冒泡排序需要几趟(index)
- 在每一趟里,又要进行几次比较。把什么进行比较呢?将【第一个(下标为0的元素)】和【第二个元素(下标为1的元素)】比较,第二个和第三个比较…(j),总是将arr[j]和arr[j+1]进行比较
- 最后打印出来。
先展示第一趟冒泡排序:看完图片之后发现,一趟冒泡排序只将一个数字放在它本应该在的位置。
第二趟冒号排序:
之后的和这类似,现在思考一下:
- 我有10个元素,需要几趟冒号排序呢?9趟(你想想,9个数字都已经在正确的位置了,最后一个元素的位置肯定正确呀)所以,sz个元素,sz-1趟
- 10个元素,第一趟进行9次比较,第二趟进行8次比较,所以:sz个元素,第一次进行sz-1次比较,第二次进行sz-2次比较…那么在循环里,如何描述几次比较呢?sz-1写成sz-1-index(第一趟index是0,这里就是sz-1),sz-2就是sz-1-1(因为此时index是1,所以是sz-1-1)
void input(int* arr, int sz); //用于输入数字
void bubble(int* arr, int sz); //用于冒泡排序
void print(int* arr, int sz); //用于打印排序好的数组
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
//输入数字
input(arr, sz);
//将乱序的数字排序
bubble(arr, sz);
//最后将升序的数字打印出来
print(arr, sz);
}
void input(int* arr, int sz) //用于输入数字
{
int index = 0;
for (index = 0; index < sz; index++)
{
scanf("%d", arr + index);
}
}
void bubble(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 = 0;
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
void print(int* arr, int sz) //用于打印排序好的数组
{
int q = 0;
for (q = 0; q < sz; q++)
{
printf("%d ", *(arr + q));
}
}
5. 二级指针
我们之前学习的都是一级指针,比如:int*,char*,float*
一级指针是用来存放(普通变量)的地址
二级指针是用来存放(一级指针变量)的地址
int* ,int**
int a = 90;
int* pa = &a; //pa是一级指针变量
这里,我将a的地址放在指针变量pa里,但是大家想想,指针变量也是变量,它也有自己的地址,我们又可以将指针变量的地址存储起来放在ppa。并且我们可以通过ppa找到最开始的普通变量a:printf(“%d”,**ppa); 也可以通过ppa改变a的值。
int* * ppa = &pa; //ppa是二级指针变量
//这个*说明ppa是指针变量
//int*说明ppa指向的pa的类型是int*
之前我们可以通过一级指针访问数组元素,但是在这里说明,二级指针和二维数组没有任何关系。
6. 指针数组
大家可以将指针当作定语,数组是主语。所以指针数组本质上是数组。
字符数组:char arr[10]; 存放字符的数组
整型数组:int arr[10]; 存放整型的数组
指针数组:它是一个存放指针的数组,它的每个元素都是指针类型(int或者char等等)。
char* arr[10] :存放字符指针的数组。int* arr[10]:存放整型指针的数组。
它的用处:(1)可以将相同类型的变量的地址存储在数组中,不用再一个一个的存储
(2)存储之后又可以遍历数组(元素是地址)再解引用打印最初的变量
int a = 90;
int b = 41;
int c = 50;
int* arr[3] = { &a,&b,&c };
int index = 0;
for (index = 0; index < 3; index++)
{
printf("%d ", *(arr[index]));
}
7. 指针数组模拟二维数组
我先创建3个一维数组,再用过int*将它们关联起来(可以达到用1个一维数组维护3个一维数组的效果)
这是模拟出来的,不是真的二维数组
int arr1[4] = { 1,2,3,4 };
int arr2[4] = { 1,1,2,4, };
int arr3[4] = { 2,4,6,8 };
int* ARR[3] = { arr1,arr2,arr3 };
//这里的arr1并不是那两种例外,所以是数组首元素的地址
//我们想通过首元素地址然后遍历这个数组的所有元素
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
printf("%d ", ARR[i][j]);
//ARR[i]等价于*(ARR+i) ,ARR[i][j]等价于*(*(ARR+I)+j)
}
}
printf("\n");
}
ARR [i] 是访问ARR数组的元素,ARR [i] 找到的数组元素指向了整型一维数组,ARR[i][j]就是整型一维数组中的元素。