1.数组名的理解
数组名代表的是数组首元素的地址,这意味着它是一个指向数组首元素的指针常量。
在上一章中我们有一段这样的代码:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int* p = &arr[0];
这里我们使用&arr[0]的方式拿到了数组第一个元素的地址,但是其实数组本身就是首元素的地址,我们来做个测试:
我们发现输出结果数组名和首元素的地址打印的一样,由此可以得出数组名就是数组首元素第一个元素的地址
下面我们在来分析下面代码:
由结果可以得知,如果arr是首元素地址那结果应该输出4/8.
其实是因为有两个例外:
1.sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
2.&数组名,这里的数组名,取出的是整个数组的地址.
其他任何地方使用数组名都表示首元素地址。
观察下面代码:
运行结果:
结果一模一样,那么arr和&arr又有什么区别呢?
下面再来看下这个代码:
输出结果:
这里不难看出&arr[0]和&arr[0]+1相差4个字节,arr和arr+1相差4个字节,但是&arr和&arr+1相差40个字节。
由此可以看出&arr得到的是整个数组的地址,而arr则等同于&arr[0]
,即数组首元素的地址。
2.使用指针访问数组
p+i 等价&arr[i]
3.一维数组传参的本质
输出结果:
可以看出函数内部并没有传进正确的个数。
当我们将一个一维数组作为参数传递给函数时,编译器会将其解析为指向数组受元素地址的指针,意味着传递的不是数组本身,而是首元素的地址。
sizeof(arr)计算的是一个地址的大小(单位是字节)而不是数组的大小(单位字节)。正是因为函数d的参数部分的本质是指针,所以在函数内部是没办法求的数组元素个数的。
总结:一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
4.冒泡排序
冒泡排序的核心思想是在每一轮遍历中,比较相邻的两个元素,如果润许有误则进行交换,通过多轮的遍历,将最大(或最小)的元素逐渐“冒泡”到数组的最后(或最前)。
算法步骤
1从数组的第一个元素开始,依次比较相邻的两个元素。
2.如果第一个元素大于第二个元素(对于升序排序),则交换它们的位置。
3.继续比较下一个相邻的元素,直到数组的末尾。
4.每完成一轮遍历,最大的元素会被放置在正确的位置。
5.重复上述步骤,直到没有需要交换的元素,即数组已经完全排序。
5.二级指针
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
二级指针图解:
在这个例子中,ppa是一个二级指针,它指向的是pa的地址
二级指针的使用包括以下几个方面:
1.初始化和赋值:二级指针可以通过取地址操作符&来初始化,例如:
int *p = &a:
int **q = &p:
这里q是一个二级指针,指向p的地址。
2.作为函数参数:二级指针可以作为函数参数传递,常用于传递指针数组或修改指针指向的情况。例如:
void swap(int **a,int**b){
int *t = *a;
*a = *b;
*b = t;
}
3访问多维数组:通过二级指针可以方便地访问多维数组。例如,假如有一个二维数组arr,可以通过二级指针来遍历数组中的每个元素。
4.解引用操作:二级指针需要进行两次解引用操作来访问其指向的一级指针所指向的数据如:
int value = **p;
这里**p表示先将p解引用得到一级指针,然后再将这个一级指针对应的值解引用得到最终的值。
5.内存分配和释放:在某些情况下,函数可能会负责分配内存,并通过二级指针返回指向新分配内存的一级指针。例如:
int* fun(){
static int data[10];
return &data[0];
}
在这个例子中,函数fun返回了一个指向静态数组data的二级指针。
6.指针数组
整型数组,是存放整形的数组,字符数组是存放字符的数组,类比就可以看出指针数组就是存放指针的数组。
77
7.指针数组模拟二维数组
parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型一维数组,parr[i][j]就是整型一维数组中的元素。
上述代码模拟出的二维数组效果,实际并非完全是二维数组,因为每一行并非是连续的。