1.字符指针
字符指针:指向字符型数据的指针变量。
每个字符串在内存中都占用一段连续的存储空间,并有唯一确定的首地址。
即将字符串的首地址赋值给字符指针,可让字符指针指向一个字符串。
一段代码说明一切
#include <stdio.h> int main() { char str1[] = "hello"; char str2[] = "hello"; const char *str3 = "hello.";//字符指针 const char *str4 = "hello.";//字符指针 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; }
2.指针数组
arr先与 [ ] 结合,所以arr是一个数组,类型为int *
1. int* arr1[10]; //整形指针的数组 2. char *arr2[4]; //一级字符指针的数组 3. char **arr3[5];//二级字符指针的数组
3.数组指针
3.1什么是数组指针??
arr先与*结合,所以P是一个指针变量,类型为int (* ) [10],大小为4*10字节
int (*p)[10];
3.2数组指针的用处
知道用处之前我们首先要先了解&arr和arr
共同点:打印出来都是数组首元素的地址
不同点:&arr表示的是整个数组的地址,但arr是首元素的地址
例如:(&arr+1)表示直接越过了整个数组,而(arr+1)表示数组中第二个元素的地址
知道这些后我们就可以想到指针数组的用处:
指针数组是用来存放数组地址的指针,可以通过加减控制
//思考一下这些的区别: int arr[5]; int *parr1[10]; int (*parr2)[10]; int (*parr3[10])[5];
4. 数组传参和指针传参
4.1一维数组传参
#include <stdio.h> void test(int arr[])//ok? {}//ok void test(int arr[10])//ok? {}//ok void test(int *arr)//ok? {}//ok void test2(int *arr[20])//ok? {}//no void test2(int **arr)//ok? {}//no int main() { int arr[10] = {0}; int *arr2[20] = {0}; test(arr); test2(arr2); }
4.2二维数组
void test(int arr[3][5])//ok? {}//ok, 但是不建议这样写,比较麻烦 void test(int arr[][])//ok? {}//no, 数组列数不可以省略 void test(int arr[][5])//ok? {}//ok, 可以省略行但不能省略列,推荐写法 //总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。 //因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。 //这样才方便运算。 void test(int *arr)//ok? {}//no, 二维数组数组名是数组第一行的地址,而一个整型指针是存不下一个一行数组地址的 void test(int* arr[5])//ok? {}//no, arr不是指针,arr先于[5]结合,是一个数组 void test(int (*arr)[5])//ok? {}//ok, 大小为5*int的数组指针,可以存下整行的地址 void test(int **arr)//ok? {}//no, 二级指针用来存放一级指针的地址,大小也为int所以存不下 int main() { int arr[3][5] = {0}; test(arr); }
4.3一级指针传参
#include <stdio.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;//p为arr的首地址,大小是int int sz = sizeof(arr)/sizeof(arr[0]);//数组元素个数的计算不能在函数内部 //一级指针p,传给函数 print(p, sz); //当然 print(arr, sz)等价于print(p, sz); return 0; }
4.4二级指针传参
#include <stdio.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; }
5.函数指针
5.1函数指针
int add(int a, int b) { return a + b; } int main() { int (*padd)(int,int) = add; //这里就是定义了一个函数指针 //这个函数指针的类型是int (* )(int,int),变量是padd //写的时候要注意指针类型的格式 printf("%d",padd(1, 2)); return 0; }
void test() { printf("hehe\n"); } int main() { void (*ptest)()=test; //定义一个函数指针ptest ptest();//打印hehe test(); //打印hehe //这里的ptest和test可以看作等价 return 0; }
5.2读懂这俩代码你就完全理解函数指针了
1. //代码1 2. (*(void (*)())0)(); 3. //代码2 4. void (*signal(int , void(*)(int)))(int);
6.函数指针数组
6.1什么是函数指针数组
可以类比指针数组进行理解,就是存放多个函数指针的数组,通过指针数组可以减少代码赘余
int (*parr1[10])(); //这就是函数指针数组 //在函数指针变量的后面加上 [ ]
6.2函数指针数组的作用
我们可以通过一个计算机实现的代码理解:
不用函数指针数组的情况(代码很长但看一眼就能明白很简单):
我们可以感受到明显的赘余,并且想添加新的功能比较麻烦
#include <stdio.h> 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 x, y; int input = 1; int ret = 0; do {//先做一个初始界面: printf( "*************************\n" ); printf( " 1:add 2:sub \n" ); printf( " 3:mul 4:div \n" ); printf( "*************************\n" ); printf( "请选择:" ); scanf( "%d", &input); switch (input)//根据你的选择来判断做什么运算 { case 1: printf( "输入操作数:" ); scanf( "%d %d", &x, &y); ret = add(x, y); printf( "ret = %d\n", ret); break; case 2: printf( "输入操作数:" ); scanf( "%d %d", &x, &y); ret = sub(x, y); printf( "ret = %d\n", ret); break; case 3: printf( "输入操作数:" ); scanf( "%d %d", &x, &y); ret = mul(x, y); printf( "ret = %d\n", ret); break; case 4: printf( "输入操作数:" ); scanf( "%d %d", &x, &y); ret = div(x, y); printf( "ret = %d\n", ret); break; case 0: printf("退出程序\n"); breark; default: printf( "选择错误\n" ); break; } } while (input); return 0; } //当我们想新加入一个功能的时候 //首先要写一个函数,还要添加新的case比较麻烦
使用函数指针数组的情况:
#include <stdio.h> 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()//可以看到main函数更加精炼 { int x, y; int input = 1; int ret = 0; int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表 //函数指针数组的使用 while (input) { printf( "*************************\n" ); printf( " 1:add 2:sub \n" ); printf( " 3:mul 4:div \n" ); printf( "*************************\n" ); printf( "请选择:" ); scanf( "%d", &input); //关键代码: if ((input <= 4 && input >= 1))//input的范围,增加函数时只需要改动这里 { printf( "输入操作数:" ); scanf( "%d %d", &x, &y); ret = (*p[input])(x, y); }//只需要通过用户选择的输入的input就可以调用p[input]函数 //如果增加新的功能,也不需要更改 else printf( "输入有误\n" ); printf( "ret = %d\n", ret); } return 0; }
7. 指向函数指针数组的指针
指向函数指针数组的指针是一个 指针
指针指向一个 数组 ,数组的元素都是 函数指针
void test(const char* str) { printf("%s\n", str); } int main() { void (*pfun)(const char*) = test; //函数指针pfun void (*pfunArr[5])(const char* str);//函数指针的数组pfunArr pfunArr[0] = test; //指向函数指针数组pfunArr的指针ppfunArr void (*(*ppfunArr)[5])(const char*) = &pfunArr;//等式左右两边是等价的 return 0; }
8. 回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
例子:使用回调函数,模拟实现qsort(采用冒泡的方式)
#include <stdio.h> int int_cmp(const void * p1, const void * p2) { return (*( int *)p1 - *(int *) p2); } void _swap(void *p1, void * p2, int size) { int i = 0; for (i = 0; i< size; i++) { char tmp = *((char *)p1 + i); *(( char *)p1 + i) = *((char *) p2 + i); *(( char *)p2 + i) = tmp; } } void bubble(void *base, int count , int size, int(*cmp )(void *, void *)) { int i = 0; int j = 0; for (i = 0; i< count - 1; i++) { for (j = 0; j<count-i-1; j++) { if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0) { _swap(( char *)base + j*size, (char *)base + (j + 1)*size, size); } } } } int main() { int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 }; //char *arr[] = {"aaaa","dddd","cccc","bbbb"}; int i = 0; bubble(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; }