文章目录
- 指针与数组
- 指针访问数组
- 一维数组传参的本质
- 冒泡排序
- 二级指针
- 指针数组
- 指针数组模拟二维数组
- 字符指针
- 数组指针
- 二维数组传参的本质
- 函数指针
- 函数指针数组
- qsort函数
指针与数组
数组的名字就是数组首元素地址。
特殊情况
- sizeof(数组名),这里的数组名是一整个数组
- &数组名,取出的是整个数组的地址
int main()
{int arr[4] = { 0 };printf("&arr[0] = %p\n",&arr[0]);//取首元素地址printf("&arr[0] + 1 = %p\n", &arr[0] + 1);//首元素地址加1printf("arr = %p\n", arr);//数组名等价于数组首元素地址printf("arr+1 = %p\n", arr + 1);printf("&arr = %p\n", &arr);//取整个数组地址printf("&arr + 1 = %p\n", &arr + 1);return 0;
}
指针访问数组
指针指向数组后,指针加一个i就是指向了这个数组的第i+1个元素(与索引相同),也可以写作P[i];
- ∗ ( P + i ) = ∗ ( a r r + i ) = a r r [ i ] = P [ i ] *(P + i) = *(arr + i) = arr[i] = P[i] ∗(P+i)=∗(arr+i)=arr[i]=P[i]
一维数组传参的本质
void test(int arr[])
{int sz2 = sizeof(arr) / sizeof(int);printf("sz2 = %d\n",sz2);
}int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };int sz1 = sizeof(arr) / sizeof(int);printf("sz1 = %d\n",sz1);test(arr);return 0;
}
数组传参的本质是传递数组首元素地址,数组形参的部分是不会真正创建数组的,所以不需要创建数组大小,所以一般传递数组的函数都需要传递数组元素。
冒泡排序
void buble_sort(int* p,int sz)
{int i = 0,j = 0;for (i = 0; i < sz - 1; i++){int flag = 1;//有序数组假设for (j = 0; j < sz - i - 1; j++){if (p[j] > p[j + 1]){int temp= 0;temp = p[j];p[j] = p[j + 1];p[j + 1] = temp;flag = 0;}}if(flag)//如果有序,就结束排序break;}
}void print(int* p, int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ",*(p+i));}printf("\n");
}int main()
{int arr[10] = { 9,1,2,3,4,5,6,7,8,0 };int sz = sizeof(arr) / sizeof(int);print(arr,sz);buble_sort(arr, sz);print(arr,sz);return 0;
}
二级指针
指针变量也是变量,他自己也有地址,所以也可以用指针指向指针变量,指向指针变量的指针叫做二级指针
int a = 10;
int* pa = &a;
int** ppa = &pa;
这里的pa存放了a的地址,而ppa存放了pa的地址
再迁移知识,如果我们要访问pa的地址
&pa;//法一
ppa;//法二
如果我们要访问pa存放的值(a的地址)
pa;//pa本身的值
*ppa;
如果我们要访问a的值
a;
*pa;
**ppa;//外层解引用,得到pa这个指针,再解引用得到a的值
指针数组
存放指针的数组,(存放地址的数组)
int* arr[2] = {addr1,addr2};
int main()
{int a = 10;int b = 11;int c = 12;int* arr[3] = {NULL};arr[0] = &a;arr[1] = &b;arr[2] = &c;for (int i = 0; i < 3; i++){printf("arr[%d] = %p\n",i,arr[i]);printf("*arr[%d] = %d\n",i,*arr[i]);}return 0;
}
指针数组模拟二维数组
int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };int* arr[] = { arr1,arr2,arr3 };return 0;
}//这并非真正的二维数组,因为空间不连续
字符指针
当字符串赋给字符指针时,其实是把字符串的首地址给了指针
因为字符串的每一个字符地址连续,而指针只能同时维护一个同类型的空间大小的栈。
#include <stdio.h>int main(){char str1[] = "hello bit.";char str2[] = "hello bit.";const char *str3 = "hello bit.";const char *str4 = "hello bit.";if(str1 ==str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if(str3 ==str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;}
运行结果为
str1 and str2 are not same
str3 and str4 are same
原因:str1与str2都是数组首地址,虽然接受同样的内容,但是同样的内容存放在不同的栈空间。
str3 与str4是指向常量的指针,也就是说str3与str4都指向相同的空间
数组指针
数组指针顾名思义就是一个指针,只是这个指针维护的空间大小是整个数组,而不是数组元素的类型,也就是说,数组指针加减1就直接跳过一个数组的长度。
int main()
{int arr[10] = { 0 };int (*p)[10] = &arr;return 0;
}
这里这里的指针类型怎么理解。
这里的p依然指向数组首地址,但是维护的空间变为了int [10],这是一个数组类型,也就是说p维护了一个数组长度的空间,还是和前面讲指针类型是一样,int [10]是指向的元素类型,*表示它是一个指针。
二维数组传参的本质
二维数组传参的本质其实是将一个数组指针传递了过去,这样指针加1就会跳过一维数组的长度
arr[i][j]
我们知道 [ i ] [i] [i]这个操作符是将指针改为 ∗ ( p t r + i ) *(ptr + i) ∗(ptr+i)的形式,那这里的arr就是一个数组指针指向有[j]个元素宽度的数组,arr[i]就是*(arr + i)用来选行,而解引用后得到了第i行的数组,[j]再将器转换为 *( *(arr + i) + j)用来选列,最外层就是对单个元素的解引用。再和数组指针联系,上一个例子中,p就相当于这里的arr,而p+i就相当于 arr + i,余下结尾相同类比
函数指针
函数也是用地址的,所以函数也能用指针指向
同样函数的名称就是一个地址,与数组一样,
int (*p)(参数列表)
与数组指针类比记忆,这里的p指向的元素类型是 int(参数列表)类型
那么我们只需要对指针进行解引用,结合普通函数使用无异。
int add_fun(int x,int y)
{return x + y;
}int main()
{int (*p)(int ,int) = add_fun;printf("%d",(*p)(20,30));return 0;
}
(*(void (*)())0)();
void (*signal(int , void(*)(int)))(int);
//解释
(void (*)())是一种强制类型转换
(void (*)())0是将0作为指针(地址),也就是假设0x00000...00有一个函数,这里的地址我作为访问它的指针
*(void (*)())0是解引用
(*(void (*)())0)();调用函数void (*signal(int , void(*)(int)))(int);是一个函数指针,它的返回类型是void ,参数为int
signal(int , void(*)(int))这个函数指针的名字,而这个名字是一个函数调用,说明次函数返回的是一个地址或指针变量
函数指针数组
函数指针数组顾名思义就是存放函数指针的数组
他的用途一般是用来实现转移表
转移表是在需要频繁实现函数跳转时的使用时,可以用函数指针数组的索引将函数的调用转换为索引值的改变。
qsort函数
qsort函数,可以实现对任意类型的数组进行排序,它需要包含头文件<stdlib.h>
qsort(void* base,int num,int size,int(*cmp)(void *e1,void *e2))
- 第一个参数是数组,无论什么类型的数组都能用void型的指针指向
- 第二个参数是 数组元素个数
- 第三个参数是数组元素的长度
- 第四个参数是一个函数指针,这个指针指向的函数需要我们自己实现,也很简单,只要第一个指针e1指向的值大于第二个指针e2指向的值就返回大于1的数,等于则返回0,小于则返回小于0的数,则qsort会帮助我们自动排序,而qsort的底层实现其实也可以自己写,比如用冒泡排序,选择排序等等,但是库函数中已经为我们定义好了这个函数,直接用就行。