一.指针相关类型
1.字符指针
1.形式及介绍:
int main() { const char* pstr="hello world"; printf("%s\n",pstr);//打印出hello world return 0; }
含义:把字符串"hello world"的首字符的地址即h的地址放在了字符指针pstr中,而不是将字符串"hello world"直接放入字符指针pstr中
2.面试题
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"); 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; }
最后打印的是:
str1 and str2 are not same
str3 and str4 are same
因为:
1.str1与str2是两个不同的数组,只不过是两者初始化的内容相同而已,但是两者所开辟的空间并不相同,因此两者"not same"
2.str3与str4指向的是同一个常量字符串,C/C++会把常量字符串存储到一个单独的内存区域,当几个指针指向同一个字符串时,它们实际指向的是同一块内存,因此两者"same"
但是用相同的常量字符串去初始化不同数组的时候就会开辟出不同的内存块,因此str1与str2 “not same”
2.指针数组
1.形式及介绍
int* arr1[10];//arr1为指针数组,数组中存放的是int*类型的指针 char* arr2[4];//一级字符指针的数组 char* *arr3[5];//二级字符指针的数组
2.应用实例1:模拟实现二维数组
那么指针数组有什么应用实例呢?
下面我们来看一下它的第一个应用实例:即模拟实现二维数组
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[3] = { arr1,arr2,arr3 }; int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { printf("%d ", arr[i][j]); } printf("\n"); } return 0; }
3.应用实例2:动态开辟二维数组
//动态开辟的二维数组 //1.开辟一个一维的指针数组 //2.开辟row个一维的数组,让指针数组中的指针指向相应的一维数组 //释放时先释放后开辟的那些一维数组,然后才能释放第一个指针数组 int** maze=(int**)malloc(sizeof(int*)*row); //开辟的指针数组,用int** 来接收指针数组的首地址,可以分解为int* *,后面那个*表示这个变量是一个指针,前面那个变量表示这个数组的元素类型是int*的指针类型 for(int i=0;i<row;i++) { maze[i]=(int*)malloc(sizeof(int)*col); //动态开辟一维数组,并用指针数组分别指向 } //二维数组的输入 for(int i=0;i<row;i++) { for(int j=0;j<col;j++) { scanf("%d",&maze[i][j]); } }
3.数组指针
1.形式及介绍
int (*p)[10]; //指向一个数组的指针,该数组的元素类型为int,所含元素个数为10 //原因: //[]的优先级高于*号,所以必须加上()来保证p先和*结合成为指针类型 //此时p的类型为int(*)[10],即去掉p后即为指针的类型
2.使用:接收二维数组传参
void print_arr2(int(*arr)[5], int row, int col) { int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { printf("%d ", arr[i][j]); } printf("\n"); } } int main() { int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 }; print_arr2(arr, 3, 5); //数组名arr表示二维数组首元素的地址 //但是二维数组的首元素是二维数组的第一行 //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址 //可以用数组指针来接收 return 0; }
3.小小总结
学习了指针数组和数组指针之后,让我们来一起回顾一下:
int arr[5]://整型数组,数组大小为5个元素,数组元素类型为int类型 int* parr1[10];//指针数组,数组大小为10个元素,数组元素类型为int*类型 int (*parr2)[10];//数组指针,指针指向一个数组,这个数组大小为10个元素,每个元素的类型是int int (*parr3[10])[5]//指针数组,数组大小为10个元素,每个元素的类型都是数组指针,这个指针指向一个数组,数组的大小为5个元素,返回类型为int
二.数组传参
1.一维数组传参
void test(int arr[])//ok? {} void test(int arr[10])//ok? {} void test(int *arr)//ok? {} void test2(int *arr[20])//ok? {} void test2(int **arr)//ok? {} int main() { int arr[10] = {0}; int *arr2[20] = {0}; test(arr); test2(arr2); }
1.ok,一维数组传参,参数用一维数组来接收 2.ok,一维数组传参,参数用一维数组来接收 3.ok,arr代表数组首元素地址,可以用int*类型的指针来接收 并通过指针进行加减整数运算来遍历整个数组中的全部元素 4.ok,arr2是指针数组,用指针数组来接收完全可以 5.ok,arr2是指针数组,其中的每个元素都是int*类型,所以用二级指针来接收完全可以
2.二维数组传参
void test(int arr[3][5])//ok? {} void test(int arr[][])//ok? {} void test(int arr[][5])//ok? {} //总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。 //因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。 //这样才方便运算。 void test(int *arr)//ok? {} void test(int* arr[5])//ok? {} void test(int (*arr)[5])//ok? {} void test(int **arr)//ok? {} int main() { int arr[3][5] = {0}; test(arr); }
1.ok,二维数组传参,用二维数组接收 2.err,用二维数组作形参时,函数形参只能省略第一个[]中的数字 因为对于一个二位数组来说,可以不知道有多少行 但是必须知道每一行有多少元素即有多少列才可以正确地确定出该二维数组的形状 3.ok,同上 4.err,int* 类型的指针只能存储整型数据与一维数组,不能存储二维数组 5.err,因为实参为二维数组,所以必须只能用二维数组或数组指针来接收, 也就是无法用指针数组来接收 实参类型为int[][5],形参类型为int*[5],不相同,无法接收 6.ok,数组指针,每个指针指向的数组:大小为5个元素,每个元素的类型为int类型,每个指针刚好可以表示这个二维数组的一行 实参类型为int[][5],形参类型为int(*)[5]即int[][5],所以可以接收 7.err,二维数组不能用二级指针来接收,二级指针是存放一级指针地址的指针,不能表示二维数组 实参类型为int[][5],形参类型为int** ,不相同,无法接收
三.指针传参
1.一级指针传参
当一个函数的参数部分为一级指针的时候,该函数能够接受什么参数? 方法:看实参类型与形参类型是否相同? 例如:以字符型指针为例 void test(char* p) {} char ch = 'a'; char* ptr = &ch; char arr[] = "abcdef"; test(&ch); test(ptr); test(arr); //这三种均可以
2.二级指针传参
当一个函数的参数部分为二级指针的时候,该函数能够接受什么参数? 方法:看实参类型与形参类型是否相同? 数组传参:形参可以写成数组形式,也可以写成指针形式 指针传参:形参只能写成指针形式 void test(char** p) {} char ch = 'a'; char* cp = &ch; char** cpp = &cp; char* p1[10]; test(cpp); test(&cp); test(p1); //这三种均可以
四.函数指针
1.形式及介绍
void test(int a,int b); void (*pf)(int,int); 此时pf就叫做函数指针,该指针指向test函数,返回值类型为void类型,参数为两个int类型的形参
2.两个有趣的代码
1.(*(void(*)())0)(); 首先考虑我们要以0为突破点 所以我们摘出0那一部分的参数 1=(void(*)())0 (*1)() 注意: void(*p)()-p是函数指针 void(*)()是函数指针类型 又因为void(*)()为:函数指针类型,该类型所指向的函数返回值为void,无参数 也就是说在这里: 1.把0强制类型转换为一个函数指针类型 2.然后调用0地址处的函数
2.void(*signal(int,void(*)(int)))(int); 首先我们还是把里面的东西(signal)先摘出来 1=signal(int,void(*)(int)) void(*1)(int) 又因为对于一个函数来说: 去掉其函数名跟形参部分,最终剩下的部分就是函数的返回值类型 所以这是一个函数signal,signal的返回值类型是一个函数指针,void(*)(int)类型 参数:第一个为int类型,第二个为一个函数指针:void(*)(int)类型
该题可以简化为下列形式: typedef void(*pf_t)(int); pf_t signal(int, pf_t);
小小复习typedef: typedef unsigned int uint; typedef int* ptr_t; typedef int(*parr_t)[10];//此时parr_t是一个数组指针类型 typedef int (*pf_t)(int, int);//此时pf_t是函数指针类型
五.函数指针数组
1.形式及介绍
先把函数指针的形式写出来 void test(int a,char b); void (*pf)(int,char);//函数指针 void (*pf[10])(int,char);//函数指针数组
2.实例应用:计算器
1.未简化版(未应用函数指针数组)
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("********0.exit 1.add *********\n"); printf("********2.sub 3.mul *********\n"); printf("******** 4.div *********\n"); printf("***********************************\n"); } void test04() { int input = 0; int x = 0; int y = 0; int ret = 0; //函数指针数组 int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div }; do { menu(); 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"); break; default: printf("选择错误,请重新选择\n"); break; } } while (input); }
2.简化版
void test04 () { int input = 0; int x = 0; int y = 0; int ret = 0; //函数指针数组 int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div }; do { menu(); printf("请选择:>"); scanf("%d", &input); if (input >= 1 && input <= 4) { printf("请输入两个操作数:"); scanf("%d%d", &x, &y); ret = pfArr[input](x, y); printf("ret=%d\n", ret); } else if (input == 0) { printf("退出计算器\n"); } else { printf("输入错误,请重新输入\n"); } } while (input); }
六.指向函数指针数组的指针
1.形式及介绍
指向函数指针数组的指针是一个指针,该指针指向一个数组,数组中的所有元素都是一个函数指针 int main() { //int (*pf)(int, int);//函数指针 int (*pfArr[])(int, int) = { Add,Sub,Mul,Div };//函数指针数组 int(*(*ppfArr)[])(int, int);//指向函数指针数组的指针 //int* pArr[] = { 1,2,3,4 };指针数组 //int(*p)[];数组指针 }
以上就是征服C语言指针系列(2)的全部内容,希望能对大家带来帮助,谢谢大家!