美丽的外貌千篇一律,有趣的灵魂万里挑一
指针是C语言的灵魂
应该有不少小伙伴对指针还是比较迷迷糊糊的吧
本期 阿紫 接着带大家一起过关斩将,擒 “ 指针 ”
目录
字符指针:指向字符的指针,指针类型为 char*
- char ch = 'a';
- char* pc = &ch;
一般使用方法:
- #include<stdio.h>
- int main()
- {
- char ch = 'a';
- char* pc = &ch;
- *pc = 'b';
- printf("%c\n", ch);
-
- return 0;
- }
还有一种使用方法:
- #include<stdio.h>
- int main()
- {
- const char* pc = "abcdef";
- printf("%s\n", pc);
-
- return 0;
- }
思考1:为什么 const char* pc = "abcdef" 前面要加 const?
答:因为字符指针 pc 指向的是常量字符串,常量是不能发生改变的,所以加 const 避免修改常量字符串。
思考2:const char* pc = "abcdef",是把 abcdef 字符串放入字符指针变量 pc 中吗?
答:不是,是把字符串中首字符的地址放入了 pc 中,也就是把 a 的地址放在 pc 中 。
面试题:
- #include<stdio.h>
- int main()
- {
- char arr[] = { "abcdef" };
- char brr[] = { "abcdef" };
- const char* crr = "abcdef";
- const char* drr = "abcdef";
- if (arr == brr)
- {
- printf("arr and brr are same\n");
- }
- else
- {
- printf("arr and brr not are same\n");
- }
- if (crr == drr)
- {
- printf("crr and drr are same\n");
- }
- else
- {
- printf("crr and drr not are same\n");
- }
-
- return 0;
- }
运行结果:
arr 和 brr 是字符数组,是把 abcdef 存储在字符数组中,它们的内存不是同一块,所以它们的地址也就不相同。而 crr 和 drr 是字符指针,是把 abcdef 常量字符串的首元素地址存储字符指针中,所以它们指向的是同一块内存空间。
指针数组:它是一个数组,数组中的每个元素都是类型指针
int* arr[10]; //整形指针的数组
首先 arr 先跟 [10] 结合说明它是一个有十个元素数组,每个元素都是 int* 类型的指针。
数组指针:是一个指针,指向的是一个数组
- int arr[6] = { 1, 2, 3, 4, 5, 6 };
- int(*pi)[6] = &arr;
pi 先和 * 结合说明它是一个指针变量,[6] 说明它指向了一个数组,数组中有 6 个元素,int 说明每个元素都是 int 类型。
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
arr 和 &arr 它们打印出来的地址虽然一样 ,但它们代表的含义不一样。arr 表示数组首元素的地址,而 &arr 表示整个数组的地址,arr + 1跳过了一个元素,而&arr + 1跳过了整个数组。
sizeof(arr[0])是计算 arr[0] 所占空间大小,而 sizeof(arr)是计算整个数组所占空间大小 。
结论:&数组名 和 sizeof(数组名) 表示整个数组,其余的数组名都表示数组首元素的地址。
既然数组指针指向的是数组,那数组指针中存放的也就是数组的地址。
- #include<stdio.h>
- int main()
- {
- int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int(*pi)[10] = &arr;
-
- return 0;
- }
把数组 arr 的地址赋值给数组指针变量 p ,但我们一般很少这样写代码 ,因为这样写代码没有任何的意义。
一般我们将数组指针用于二维数组
- #include<stdio.h>
- void print_arr(int(*arr)[3], int row, int col)
- {
- for (int i = 0; i < row; i++)
- {
- for (int j = 0; j < col; j++)
- {
- printf("%d ", arr[i][j]);
- }
- printf("\n");
- }
- }
- int main()
- {
- int arr[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
- print_arr(arr, 3, 3);
-
- return 0;
- }
数组名arr,表示首元素的地址 。但是二维数组的首元素是二维数组的第一行 ,所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址。
学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:
- int arr[5];//整型数组
- int *parr1[10];//数组指针
- int (*parr2)[10];//指针数组
- int (*parr3[10])[5];//???
int (*parr3[10])[5]:首先 parr3 跟 [10] 结合说明它是一个数组,每个元素都是指针数组类型。
函数指针:首先是它是一个指针,它指向的是一个函数
函数名和&函数名:都表示函数的地址
要是想保存函数的地址,就得用到函数指针
p 先跟 * 结合 说明它是指针,后面的一个 () 表示它指向了一个无参的函数,前面的 void 表示返回值 void。
《 C陷阱和缺陷 》 这本书中有这样两个代码:
- //代码1
- (*(void (*)())0)();
将 0 强制类型转换为 void(*)() 函数指针类型,那么 0 就被当成了一个函数的地址,然后解引用调用,实际上这就是一次函数调用,调用的是 0 作为地址处的函数。
- //代码2
- void (*signal(int , void(*)(int)))(int);
signal 先跟后面的括号结合,说明 signal 是函数名,函数参数第一个参数是 int,第二个参数是函数指针(该函数指针指向的函数参数是int,返回类型是void),返回值也是一个函数指针,该函数指针指向的函数参数是int,返回类型是void,所以这是一次函数声明。
函数指针数组:首先它是一个数组,数组的每一个元素都是指针指向的是函数。
int(*arr[10])();
首先 arr 先跟 [10] 结合说明它是一个数组,数组中的每一个元素都是 int(*)()类型的函数指针。
函数指针数组的用途:转移表
- #include<stdio.h>
- void menu()
- {
- printf("*********************\n");
- printf("*** 1.add 2.sub ***\n");
- printf("*** 3.mul 4.div ***\n");
- printf("****** 0.exit *******\n");
- printf("*********************\n");
- }
- int add(int a, int b)
- {
- return a + b;
- }
- int sub(int a, int b)
- {
- return a - b;
- }
- int mul(int a, int b)
- {
- return a * b;
- }
- int div(int a, int b)
- {
- return a / b;
- }
-
- int main()
- {
- int a, b;
- int input = 1;
- int ret = 0;
- int(*p[5])(int a, int b) = { 0, add, sub, mul, div }; //转移表
- while (input)
- {
- menu();
- printf("请选择:");
- scanf("%d", &input);
- if ((input <= 4 && input >= 1))
- {
- printf("输入操作数:");
- scanf("%d %d", &a, &b);
- ret = (*p[input])(a, b);
- }
- else
- printf("输入有误\n");
- printf("ret = %d\n", ret);
- }
- return 0;
- }
指向函数指针数组的指针 :首先它是一个指针,它指向了一个数组,数组中每个元素都是函数指针。
- void(*p)();//函数指针
- void(*p1[1])();//函数指针数组
- void(*(*p2)[2])();//指向函数指针数组指针
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数 的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进 行响应。
qosrt 函数使用:
- #include <stdio.h>
- //qosrt函数的使用者得实现一个比较函数
- int int_cmp(const void * p1, const void * p2)
- {
- return (*( int *)p1 - *(int *) p2);
- }
- int main()
- {
- int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
- int i = 0;
-
- qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);//把一个函数地址传给另一个函数
- for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
- {
- printf( "%d ", arr[i]);
- }
- printf("\n");
- return 0;
- }