字符指针变量
我们先看一行代码:
const char* pstr = "hello bit.";//这⾥是把⼀个字符串放到pstr指针变量⾥了吗?
事实上它的本质是把⼀个字符串常量hello bit的⾸字符 h 的地址存放到指针变量 pstr 中。
剑指offer中经典题:
#include <stdio.h> 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; }
会出现以上的结果是因为:C/C++会把常量字符串存储到单独的⼀个内存区域,当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
数组指针变量
学习数组指针变量之前请先记住以下两点:
数组指针 != 指针数组
指针数组是存放指针的数组,数组指针是存放整个数组的地址,而非数组首元素地址
格式:数组类型(*数组指针变量名)[数组元素个数] = &数组名
!!!一定要加上(),否则就会变成int* p[10],这是指针数组而不是数组指针!!!
作用:存放整个数组的地址
实例:
Int arr[10];
Int(*p)[10] = &arr;
加上()是因为[]的优先级要⾼于*号,所以必须加上()来保证p先和*结合形成(*p),此时p为数组指针变量,指向的是一个元素个数为10个的整型数组
#include <stdio.h> int main() { int arr[10] = { 0 }; int(*p)[10] = &arr; return 0; }
调试也能看到数组指针变量p存储了整型数组arr的地址,且二者的类型也是一样的......
⼆维数组传参的本质
过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的:
#include <stdio.h> void test(int a[3][5], int r, int c) { int i = 0; int j = 0; for(i=0; i<r; i++) { for(j=0; j<c; j++) { printf("%d ", a[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}}; test(arr, 3, 5); return 0; }
上述代码的写法是实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?是否可以利用之前学过的数组指针变量的知识来写?
关于第二种传参方式的书写思路如下:
①⼆维数组的⾸元素即第⼀⾏的⼀维数组
②由于数组名是数组⾸元素的地址,故⼆维数组的数组名表⽰的就是第一个⼀维数组首元素的地址
③我们用一个数组指针变量指向二维数组的数组名,这样就可以遍历二维数组中的每一个元素
#include <stdio.h> void test(int (*p)[5], int r, int c) { int i = 0; int j = 0; for(i=0; i<r; i++) { for(j=0; j<c; 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}}; test(arr, 3, 5); return 0; }
结论:⼆维数组传参,形参的部分可以写成数组或指针的形式
函数指针变量
格式:函数类型(指针变量)(函数形参类型) = 函数名
只需要写函数形参类型即可,不需要再写形参名
我们先来看一段代码:
#include <stdio.h> void test() { printf("hehe\n"); } int main() { printf("test: %p\n", test); printf("&test: %p\n", &test); return 0; }
通过上述代码我们可以发现:函数是有地址的,函数名就是函数的地址
与之前的数组指针变量类似,函数指针变量应该是⽤来存放函数地址的,后续可以通过地址来调⽤函数的:
#include <stdio.h> int Add(int x, int y) { return x + y; } int main() { int(*pf3)(int, int) = Add; printf("%d\n", (*pf3)(2, 3)); printf("%d\n", pf3(3, 5)); return 0; }
上述代码就是对函数指针变量的使用了......
《C陷阱和缺陷》中有这样一段代码:
Void (* signal(int ,void(*)(int) ) (int)
首先我们可以理解的是signal是一个函数名,(int , void(*)(int))是signal函数的两个参数,一个是int型,另一个是函数指针类型,该函数指针类型指向的函数参数是int型,返回类型是void,signal(int,void(*)(int))就相当于对signal函数的声明,如果我们把这个函数声明的整体假设为m,那么这行代码就会变成void(* m)(int),*m就是一个新的函数指针,该函数指针指向的函数参数是int型,返回的类型为void型。
typedef关键字
作用:类型重命名的,可以将复杂的类型简单化
⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤:
typedef unsigned int uint; //将unsigned int 重命名为uint
同样的我们也可以将指针类型重命名,将 int* 重命名为 ptr_t ,这样写:
Typedef int* ptr_t;
但是对于数组指针和函数指针稍微有点区别,⽐如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:
typedef int(*parr_t)[5]; //新的类型名必须在*的右边
函数指针类型的重命名也是⼀样的,⽐如将 void(*)(int) 类型重命名为 pf_t ,就可以这样写:
typedef void(*pfun_t)(int);//新的类型名必须在*的右边
函数指针数组
什么是函数指针数组?
函数指针数组就是把多个函数的地址存放在一个数组中
格式:返回类型(*数组名[函数个数])(形参类型,形参类型) = (函数名,函数名)
int (*parr1[3])(int,int) = (Add,Sub);
其中,parr1 [3]表示一个有三个元素的数组,数组类型就是 int (*)(int,int) ,指向(int,int)返回值是int型的函数指针变量,数组元素为Add 和 Sub
为什么要使用函数指针数组?
当函数的返回值一样时为了简化代码可以将函数指针放在指向同一个指针类型的数组中,比如:
#include <stdio.h> int Add(int x, int y) { return x + y; } int Sub(int x, int y) { return x - y; } int main() { int* arr[10]; int (*pArr[4])(int, int) = { Add,Sub }; return 0; }
转移表
计算器的⼀般实现:
#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(" 0:exit \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"); break; default: printf("选择错误\n"); break; } } while (input); return 0; }
使用函数指针实现:
//使⽤函数指针数组的实现: #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; int(*p[])(int x, int y) = { 0, add, sub, mul, div }; //转移表 do { printf("*************************\n"); printf(" 1:add 2:sub \n"); printf(" 3:mul 4:div \n"); printf (" 0:exit \n"); printf("*************************\n"); printf( "请选择:" ); scanf("%d", &input); if ((input <= 4 && input >= 1)) { printf( "输⼊操作数:" ); scanf( "%d %d", &x, &y); ret = p[input](x, y); printf( "ret = %d\n", ret); } else if(input == 0) { printf("退出计算器\n"); } else { printf( "输⼊有误\n" ); } }while (input); return 0; }
~over~