大纲
字符指针
数组指针
指针数组
数组传参和指针传参
函数指针
函数指针数组
指向函数指针数组的指针
回调函数
指针和数组面试题的解析
指针的概念(基础):
指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
指针的大小是固定的4/8个字节(32位平台/64位平台)。
指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
指针的运算。
字符指针 在指针的类型中我们知道有一种指针类型为字符指针char*
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int main () { char ch = 'z' ; char * pc = &ch; *pc = 'y' ; return 0 ; }
其他用法
1 2 3 4 5 6 7 8 9 10 11 12 13 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int main () { char arr[] = "abcdef" ; char * pc = arr; printf ("%s\n" ,arr); printf ("%s\n" , pc); printf ("%c\n" , *pc); return 0 ; }
代码char* pc = arr
; 特别容易被误以为是把字符串abcdef
放到字符指针pc
里了,但是/本质是把字符串abcdef
首字符的地址放到了pc
中。
1 2 3 4 5 6 7 8 9 10 11 12 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int main () { const char * pc = 'abc' ; printf ("%s\n" , pc); return 0 ; }
面试题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int main () { char str1[] = "helloChaser" ; char str2[] = "helloChaser" ; char * str3 = "helloChaser" ; char * str4 = "helloChaser" ; if (str1 == str2) printf ("str1 and str2 are same\n" ); else printf ("str1 and str2 are not same\n" ); if (str3 == str4) { printf ("str3 and str4 are same\n" ); } else printf ("str3 and str4 are not same\n" ); return 0 ; }
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。
指针数组 指针数组是一个存放指针的数组
1 2 3 4 5 6 7 8 9 10 11 12 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int main () { int arr[10 ] = { 0 }; char ch[5 ] = { 0 }; int * parr[4 ]; char * pch[5 ]; return 0 ; }
指针数组常见用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int main () { int arr1[] = { 1 , 2 , 3 , 4 , 5 }; int arr2[] = { 2 , 3 , 4 , 5 , 6 }; int arr3[] = { 3 , 4 , 5 , 6 , 7 }; int * parr[] = { arr1, arr2, arr3 }; int i = 0 ; for (i = 0 ; i < 3 ; i++) { int j = 0 ; for (j = 0 ; j < 5 ; j++) { printf ("%d " , *(parr[i] + j)); } printf ("\n" ); } return 0 ; }
数组指针 数组指针的定义 我们已经熟悉: 整形指针: int * pint
; 能够指向整形数据的指针。 浮点型指针: float * pf;
能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。
1 2 3 int *p1[10 ];int (*p2)[10 ];
解释:
1 2 3 4 int (*p)[10 ]; 指针,指向一个数组,叫数组指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int main () { int arr[10 ] = {0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 }; int (*p)[10 ]= &arr; return 0 ; }
解释图例
&数组名和数组名 我们知道arr是数组名,数组名表示数组首元素的地址。
1 2 3 4 5 6 7 8 9 #include <stdio.h> int main () {int arr[10 ] = {0 };printf ("%p\n" , arr);printf ("%p\n" , &arr);return 0 ; }
可见数组名和&数组名打印的地址是一样的。继续深入:
1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> int main () { int arr[10 ] = { 0 }; printf ("arr = %p\n" , arr); printf ("&arr= %p\n" , &arr); printf ("arr+1 = %p\n" , arr + 1 ); printf ("&arr+1= %p\n" , &arr + 1 ); return 0 ; }
✏️根据上面的代码我们发现:
其实&arr和arr,虽然值是一样的,但是意义应该不一样的。
实际上: &arr 表示的是数组的地址,而不是数组首元素的地址。
数组的地址+1,跳过整个数组的大小,所以&arr+1 相对于&arr 的差值是40.
数组指针的使用 既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int main () { int arr[10 ] = { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 }; int *p = arr; int i = 0 ; for ( i = 0 ; i < 10 ; i++) { printf ("%d " ,*(p+i)); } return 0 ; }
一个数组指针的使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> void print1 (int arr[3 ][5 ], int x, int y) { int i = 0 ; int j = 0 ; for (i = 0 ; i < x; i++) { for (j = 0 ; j < y; j++) { printf ("%d " , arr[i][j]); } printf ("\n" ); } }void print2 (int (*p)[5 ],int x,int y) { int i = 0 ; for ( i = 0 ; i < x; i++) { int j = 0 ; for ( j = 0 ; j < y; j++) { printf ("%d " , p[i][j]); } printf ("\n" ); } }int main () { int arr[3 ][5 ] = { { 1 , 2 , 3 , 4 , 5 }, { 2 , 3 , 4 , 5 , 6 }, { 3 , 4 , 5 , 6 , 7 } }; print2(arr, 3 , 5 ); return 0 ; }
🌷回顾 1 2 3 4 int arr[5 ];int *parr1[10 ];int (*parr2)[10 ];int (*parr3[10 ])[5 ];
数组/指针参数 一维数组传参 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> void test (int arr[]) {}void test (int arr[10 ]) {}void test (int *arr) {}void test2 (int *arr[20 ]) {}void test2 (int **arr) {}int main () { int arr[10 ] = { 0 }; int *arr2[20 ] = { 0 }; test(arr); test2(arr2); }
二维数组传参 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> void test (int arr[3 ][5 ]) {}void test (int arr[][]) {}void test (int arr[][5 ]) {}void test (int *arr) {}void test (int * arr[5 ]) {}void test (int (*arr)[5 ]) {}void test (int **arr) {}int main () { int arr[3 ][5 ] = { 0 }; test(arr); return 0 ; }
一级指针传参 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> void print (int *p, int sz) { int i = 0 ; for (i = 0 ; i<sz; i++) { printf ("%d\n" , *(p + i)); } }int main () { int arr[10 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 }; int *p = arr; int sz = sizeof (arr) / sizeof (arr[0 ]); print(p, sz); return 0 ; }
思考: 当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> void test1 (int * p) {}void test2 (char * p) {}int main () { int a = 10 ; int * p1 = &a; test1(&a); test1(p1); char ch = 'z' ; char * pc = &ch; test2(&ch); test2(pc); }
二级指针传参 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> void test (int ** ptr) {printf ("num = %d\n" , **ptr); }int main () {int n = 10 ;int *p = &n;int **pp = &p; test(pp); test(&p);return 0 ; }
思考: 当一个函数的参数部分为二级指针的时候,函数能接收什么参数?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> void test (char **p) { }int main () { char c = 'b' ; char *pc = &c; char **ppc = &pc; char * arr[10 ]; test(&pc); test(ppc); test(arr); return 0 ; }
函数指针 例1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int Add (int x ,int y) { int z = 0 ; z = x + y; return z; }int main () { int a = 10 ; int b = 20 ; printf ("%p\n" , Add); printf ("%p\n" ,&Add); return 0 ; }
例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> void Print (char * str) { printf ("%s\n" , str); }int main () { void (*p)(char *) = Print; (*p)("Be Happy" ); return 0 ; }
函数地址保存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int Add (int x, int y) { int z = 0 ; z = x + y; return z; }int main () { int a = 10 ; int b = 20 ; int (*pa)(int , int ) = Add; printf ("%d\n" , (*pa)(512 , 8 )); return 0 ; }
不寒而🌰 1 2 3 4 5 6 7 8 9 10 11 12 13 14 (*(void (*)())0 )();void (*signal(int , void (*)(int )))(int );typedef void (*pfun_t ) (int ) ;pfun_t signal (int , pfun_t ) ;
函数指针数组 数组是一个存放相同类型数据的存储空间
函数指针的数组定义 1 2 3 int (*parr1[10 ]])();int *parr2[10 ]();int (*)() parr3[10 ];
答案是:parr1
parr1 先和[] 结合,说明parr1是数组
数组的内容是是: int (*)()
类型的函数指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int Add (int x, int y) { return x + y; }int Sub (int x, int y) { return x - y; }int Mul (int x, int y) { return x * y; }int Div (int x, int y) { return x / y; }int main () { int * arr[5 ]; int (*pa)(int , int ) = Add; int (*parr[4 ])(int , int ) = { Add, Sub, Mul, Div }; int i = 0 ; for (i = 0 ; i < 4 ; i++) { printf ("%d\n" , parr[i](2 , 3 )); } return 0 ; }
小练习 1 2 3 4 5 6 7 8 char * my_strcpy (char * dest, const char * src) ;1. char * (*pf)(char *, const char *);2. char * (*pfArr[4 ])(char *, const char *);
函数指针数组的用途:转移表
代码实例:(简易计算器) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> void menu () { printf ("*************************\n" ); printf (" 1:add 2:sub \n" ); printf (" 3:mul 4:div \n" ); printf (" 5.XOR 0:exit\n" ); printf ("*************************\n" ); }int Add (int x, int y) { return x + y; }int Sub (int x, int y) { return x - y; }int Mul (int x, int y) { return x * y; }int Div (int x, int y) { return x / y; }int XOR (int x, int y) { return x ^ y; }int main () { int input = 0 ; int x = 0 ; int y = 0 ; int (*pfArr[])(int , int ) = { 0 , Add, Sub, Mul, Div, XOR }; do { menu(); printf ("请选择:>\n" ); scanf ("%d" , &input); if (input >= 1 && input<=5 ) { printf ("请输入两个操作数:>\n" ); scanf ("%d%d" , &x, &y); int ret = pfArr[input](x, y); printf ("%d\n" , ret); } else if (input == 0 ) { printf ("退出计算思密达!\n" ); } else { printf ("What are you 输啥嘞!\n" ); } } while (input); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 void menu () { printf ("*************************\n" ); printf (" 1:add 2:sub \n" ); printf (" 3:mul 4:div \n" ); printf (" 5.XOR 0:exit\n" ); printf ("*************************\n" ); }int Add (int x, int y) { return x + y; }int Sub (int x, int y) { return x - y; }int Mul (int x, int y) { return x * y; }int Div (int x, int y) { return x / y; }int XOR (int x, int y) { return x ^ y; }void Calc (int (*pf)(int , int )) { int x = 0 ; int y = 0 ; printf ("请输入两个操作数:>\n" ); scanf ("%d%d" , &x, &y); printf ("结果为 = %d\n" , pf(x, y)); }int main () { int input = 0 ; do { menu(); printf ("请选择:>\n" ); scanf ("%d" , &input); switch (input) { case 1 : Calc(Add); break ; case 2 : Calc(Sub); break ; case 3 : Calc(Mul); break ; case 4 : Calc(Div); case 5 : Calc(XOR); break ; case 0 : printf ("退出计算思密达!\n" ); break ; default : printf ("What are you 输啥嘞!\n" ); break ; } } while (input); }
指向函数指针数组的指针 指向函数指针数组的指针是一个指针
指针指向一个数组
,数组的元素都是函数指针
;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int main () { int arr[10 ] = { 0 }; int (*p)[10 ] = &arr; int (*pf)(int , int ); int (*pfArr[4 ])(int , int ); int (*(*ppfArr[4 ]))(int , int ) = &pfArr; return 0 ; }
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> void print (char *str) { printf ("this is:%s" , str); }void test (void (*p)(char *)) { printf ("test\n" ); p("test" ); }int main () { test(print); return 0 ; }
qsort函数的使用: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> struct Stu { char name[20 ]; int age; };int cmp_int (const void * e1, const void * e2) { return *(int *)e1 -*(int *)e2; }int cmp_float (const void *e1, const void *e2) { return ((int )(*(float *)e1 - *(float *)e2)); }int cmp_stu_by_age (const void * e1, const void * e2) { return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age; }int cmp_stu_by_name (const void *e1, const void * e2) { return strcmp (((struct Stu*)e1)->name, ((struct Stu*)e2)->name); }void test1 () { int arr[10 ] = { 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 }; int sz = sizeof (arr) / sizeof (arr[0 ]); qsort(arr, sz, sizeof (arr[0 ]), cmp_int); int i = 0 ; for (i = 0 ; i < sz; i++) { printf ("%d " , arr[i]); } }void test2 () { float f[] = { 9.0 , 8.0 , 7.0 , 6.0 , 5.0 , 4.0 }; int sz = sizeof (f) / sizeof (f[0 ]); qsort(f, sz, sizeof (f[0 ]), cmp_float); int j = 0 ; for (j = 0 ; j < sz; j++) { printf ("%f " , f[j]); } }void test3 () { struct Stu s [3] = { { "zhangsan" , 20 }, { "lisi" , 30 }, { "wangwu" , 10 } }; int sz = sizeof (s) / sizeof (s[0 ]); qsort(s, sz, sizeof (s[0 ]), cmp_stu_by_name); }int main () { test3(); return 0 ; }
使用回调函数,模拟实现qsort(采用冒泡的方式)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> struct Stu { char name[20 ]; int age; };int cmp_stu_by_age (const void * e1, const void * e2) { return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age; }int cmp_stu_by_name (const void *e1, const void * e2) { return strcmp (((struct Stu*)e1)->name, ((struct Stu*)e2)->name); }int cmp_int (const void * e1, const void * e2) { return *(int *)e1 - *(int *)e2; }void Swap (char * buf1, char *buf2, int width) { int i = 0 ; for (i = 0 ; i < width; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; } }void bubble_sort (void *base, int sz, int width, int (*cmp)(void *e1, void *e2)) { int i = 0 ; for (i = 0 ; i < sz - 1 ; i++) { int j = 0 ; for (j = 0 ; j < sz - 1 - i; j++) { if (cmp((char *)base + j*width, (char *)base + (j + 1 )*width) > 0 ) { Swap((char *)base + j*width, (char *)base + (j + 1 )*width, width); } } } }void test5 () { struct Stu s [3] = { { "zhangsan" , 20 }, { "lisi" , 30 }, { "wangwu" , 10 } }; int sz = sizeof (s) / sizeof (s[0 ]); bubble_sort(s, sz, sizeof (s[0 ]), cmp_stu_by_name); }int main () { test5(); return 0 ; }