目录
一、字符指针一道题二、 指针数组解引用打印数组元素&数组名 数组名字符指针初始化三、数组指针取数组地址-放在数组指针里类型数组指针的使用二维数组传参判断四、数组参数、指针参数1、一维数组传参2、二维数组传参3、一级指针传参当一个函数的参数部分为一级指针的时候,函数能接收什么参数4、二级指针传参五、函数指针1、函数指针变量 - 存放函数的地址2、类型 变量3、调用4、有趣的代码六、函数指针数组实现加减乘除六、回调函数七、指向函数指针数组的指针一、字符指针
一道题
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int main(){const str1[] = {"hello bit." };const 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 same\n");//常量字符串 -不能修改的,可以加上const//str3 str4内容一样 指向同一字符串首地址return 0;}//《剑指offer》
str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针,指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。输出:str1 and str2 are not same str3 and str4 are same
二、 指针数组
指针数组 - 是一个存放指针的数组
char* arr[5]; //ar是存放字符指针的数组int* arr2[4]; //arr2是存放整形指针的数组
解引用打印数组元素
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int main(){int a = 10;int b = 20;int c = 30;int d = 40;int* arr[4] = {&a, &b, &c, &d };int i = 0;for (i = 0; i < 4; i++){printf("%d ", *(arr[i]));}return 0;}
&数组名 数组名
//#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.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 };//数组名是首元素地址 arr1是int*类型int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", parr[i][j]); // p[i] == *(p+i)// parr[i][j] == *(parr[i]+j)}printf("\n");}return 0;}
总结
&arr和arr,它们的值是一样的,但是意义其实是不一样的:
&arr 表示数组的地址,而不是数组首元素的地址
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40
字符指针初始化
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int main(){const char* arr[5] = {"abcdef", "bcd", "hehe", "haha", "hello" };//存放每个字符串的首元素地址//常量字符串 不能改 加constint i = 0;for (i = 0; i < 5; i++){printf("%s\n", arr[i]);}return 0;}
三、数组指针
数组指针是指针,还是数组:
//是指针int* p; //整形指针 - 指向整形的指针
数组指针:能够指向数组的指针。
取数组地址-放在数组指针里
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int main(){int a = 10;int* pi = &a; //整形的地址存放在整形指针中char ch = 'w';char* pc = &ch; //字符的地址存放在字符指针里int arr[10] = {0 };int* p = arr; //数组首元素的地址//int* parr[10]; 这样写是数组int (*parr)[10] = &arr; //取出的是数组的地址,应该存放在数组指针里return 0;}
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int main(){int arr[10] = {0 };//arr; //数组名是首元素的地址//&arr[0]; //首元素的地址////&arr; //取出数组的地址printf("%p\n", arr); //arr类型是int*printf("%p\n", &arr[0]); //int*printf("%p\n", &arr); //数组的地址类型:int(*)[10] //int(*p)[10] = &arr;printf("%p\n", arr+1);printf("%p\n", &arr[0]+1);printf("%p\n", &arr+1);return 0;}
类型
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int main(){int* arr[10];int* (*p)[10] = &arr;return 0;}
数组指针的使用
下标
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>void print1(int arr[], int sz){int i = 0; for (i = 0; i < sz; i++){printf("%d ", arr[i]);}}
指针
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>void printf2(int* arr, int sz){int i = 0; for (i = 0; i < sz; i++){printf("%d ", *(arr + i));}}
数组指针
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>void print3(int(*parr)[10], int sz){int i = 0;for (i = 0; i < sz; i++){printf("%d ", parr[0][i]); //从第一个元素向后找printf("%d ", *(parr +0)[i]);printf("%d ", *(parr)[i]); //(*parr) 相当于parr指向的数组的数组名}}int main(){int arr[10] = {1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);print1(arr, sz);print2(arr, sz);print3(&arr, sz); //一维数组不适合return 0;}
二维数组传参
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>void print1(int arr[3][5], int r, int c){int i = 0;for (i = 0; i < r; i++){int j = 0; for (j = 0; j < c; j++){printf("%d ", arr[i][j]);}printf("\n");}}void print2(int(*p)[5], int r, int c){int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){//*(p + i)//加i找到行 加j找到元素 解引用//printf("%d ", *(*p + i) + 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 };print1(arr, 3, 5); //二维数组传参printf("\n");print2(arr, 3, 5); //首元素地址 指针接受//二维数组 三个元素 是一维数组 传的是一维数组的地址//首元素就是二维数组的第一行 1 2 3 4 5//*p指针 5个元素 每个元素是int类型return 0;}
判断
int arr[5]; //整型数组int *parr1[10]; //parr1是一个数组,10个元素,每个元素是int*的,所以parr1是一个存放指针的数组int (*parr2)[10]; //parr2是一个数组指针,该指针指向的数组有10个元素,每个元素是int的int (*parr3[10])[5]; //parr3是一个数组,数组有10个元素,每个元素石神一个数组指针,//该指针指向的数组有5个元素,每个元素是int
四、数组参数、指针参数
1、一维数组传参
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>void test(int arr[])void test(int* p){}int main(){int arr[10] = {0 };test(arr);return 0;}
//#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>void test(int** p){}int main(){int* arr[10] = {0 }; //10个元素 每个元素是int*类型 首元素地址就是int*的地址 用二级指针接收test(arr);return 0;}
2、二维数组传参
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>void test(int arr[3][5]) {}void test(int arr[][5]) {} //不能省略列 可以省略行//用指针方式传//二维数组传首元素的时候,指的是第一行 //用指向数组的是指针接收void test(int(*p)[5]) {} //*p指针 5个元素 每个元素是intint main(){int arr[3][5] = {0 };test(arr);return 0;}
3、一级指针传参
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>void print(int* ptr, int sz){int i = 0;for (i = 0; i < sz; i++){printf("%d ", *(ptr + i));}}int main(){int arr[] = {1,2,3,4,4,5,6,7,8,9,10 };int* p = arr;int sz = sizeof(arr) / sizeof(arr[0]);print(p, sz);return 0;}
当一个函数的参数部分为一级指针的时候,函数能接收什么参数
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>//int* p可以是什么传的void test(int* p){}int main(){int a = 10;int* p1 = &a;int arr[10] = {0 };test(&a);test(arr);test(p1);return 0;}
4、二级指针传参
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>//当函数的参数为二级指针的时候,可以接收什么参数void test(int** ppa){}int main(){int a = 10;int* pa = &a;int** ppa = &pa;test(ppa);test(&pa);int* arr[5];test(arr); //指针数组的地址return 0;}
五、函数指针
1、函数指针变量 - 存放函数的地址
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int Add(int x, int y){return x + y;}int main(){printf("%p\n", &Add);//函数名 == &函数名printf("%p\n", Add);return 0;}
2、类型 变量
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int Add(int x, int y){return x + y;}int main(){int (*pf)(int, int) = &Add; //pf是用来存放函数的地址- pf就是函数指针变量//*pf指针存放地址 指向Add,参数类型是int 返回类型也是intint arr[10] = {0 };int(*parr)[10] = &arr; //parr是数组指针变量//类型int a = 10; //intint arr[10] = {0 }; //int [10]int(*parr)[10] = &arr; //int(*)[10]int (*pf)(int, int) = &Add; //int(*)(int, int)return 0;}
3、调用
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int Add(int x, int y){return x + y;}//1int main(){int (*pf)(int, int) = &Add;int ret = Add(2, 3);printf("%d\n", ret); //5ret = (*pf)(4, 5);printf("%d\n", ret); //9return 0;}//2int main(){int (*pf)(int, int) = Add;int ret = Add(2, 3);printf("%d\n", ret); //5//ret = (*pf)(4, 5);ret = pf(4, 5); //*可省略printf("%d\n", ret); //9return 0;}
4、有趣的代码
代码1
//1#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int main(){(*(void (*)()) 0) ();//void (*ptr)();//0强制类型转换成函数指针类型void(*)() 把0当做地址 指向了无参无返回值的函数,然后解引用调用这个函数//代码是一次函数调用//1. 代码中把0强制类型转换成类型为void(*)()的一个函数的地址//2. 解引用0地址 就是取0地址的这个函数 被调用的函数是无参 返回类型是voidreturn 0;}//《C陷阱与缺陷》
代码1
//2#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int main(){void(*signal(int, void(*)(int)))(int);//此代码是一次函数声明//声明的函数名是signal//signal函数有2个参数,第一个:int类型 第二个:void(*)(int)的函数指针类型//signal函数的返回类型依然是:void(*)(int)的函数指针类型return 0;}
代码2简化
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>//代码2简化typedef void(*pfun_t)(int);int main(){pfun_t signal(int, pfun_t);return 0;}
六、函数指针数组
数组是一个存放相同类型数据的存储空间,我们已经学习了指针数组:
int* arr[10]; //整型指针的数组//函数指针的数组 - 存放函数指针的数组
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int Add(int x, int y) //int (*)(int, int){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 (*pf1)(int, int) = Add;int (*pf2)(int, int) = Sub;//pfArr就是一个函数指针的数组int (*pfArr[4])(int, int, int, int) = {Add, Sub, Mul, Div };return 0;}
实现加减乘除
函数指针数组的用途:转移表
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.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;}void menu(){printf("**********************************\n");printf("******* 1. add 2. sub *******\n");printf("******* 3. mul 4. div *******\n");printf("******* 0. exit *******\n");printf("**********************************\n");}int main(){int input = 0;do{int x = 0;int y = 0;int ret = 0;menu();printf("请选择:>\n");scanf("%d", &input);switch (input){case 1:printf("请输入2个操作数:>\n");scanf("%d %d", &x, &y);ret = Add(x, y);printf("ret=%d\n", ret);break;case 2:printf("请输入2个操作数:>\n");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("ret=%d\n", ret);break;case 3:printf("请输入2个操作数:>\n");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("ret=%d\n", ret);break;case 4:printf("请输入2个操作数:>\n");scanf("%d %d", &x, &y);ret = Div(x, y);printf("ret=%d\n", ret);break;case 0:printf("退出计算机\n");break;default:printf("选择错误,请重新选择!\n");break;}} while (input);return 0;}
优化
使用函数指针数组的实现:
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.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;}void menu(){printf("**********************************\n");printf("******* 1. add 2. sub *******\n");printf("******* 3. mul 4. div *******\n");printf("******* 0. exit *******\n");printf("**********************************\n");}int main(){int input = 0;do{int x = 0;int y = 0;int ret = 0;menu();printf("请选择:>\n");scanf("%d", &input);//《C和指针》int (*pfArr[5])(int, int) = {0, Add,Sub,Mul,Div };//0 1 2 3 4if (input == 0){printf("退出计算器\n");}else if (input >= 1 && input <= 4){printf("请输入2个操作数:>");scanf("%d %d", &x, &y);ret = pfArr[input](x, y);printf("%d\n", ret);}else{printf("选择错误\n");}} while (input);return 0;}
函数
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.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;}void menu(){printf("**********************************\n");printf("******* 1. add 2. sub *******\n");printf("******* 3. mul 4. div *******\n");printf("******* 0. exit *******\n");printf("**********************************\n");}void Calc(int(*pf)(int, int)){int x = 0;int y = 0;int ret = 0;printf("请输入2个操作数:>\n");scanf("%d %d", &x, &y);ret = pf(x, y);printf("ret=%d\n", ret);}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);break;case 0:printf("退出计算机\n");break;default:printf("选择错误,请重新选择!\n");break;}} while (input);return 0;}
六、回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
七、指向函数指针数组的指针
指向函数指针数组的指针是一个 指针 指针指向一个 数组 , 数组的元素都是 函数指针
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int main(){int arr[10];int(*p)[10] = &arr;//p是一个指向 整型数组 的指针int* arr[10]; //整型指针的数组int* (*p)[10] = &arr; //整型指针数组的地址//p是一个指向 整型指针数组 的指针int Add(int x, int y){return x + y;}int (*pf)(int, int) = Add; //pf是函数指针int (*pfArr[5])(int, int); //pfArr是一个函数指针的数组int (*(*ppfArr)[5](int, int) = &pfArr;//ppfArr是一个指向函数指针数组的指针return 0;}