1.指针思维导图:
2.字符指针
字符指针没什么好讲的,主要就强调一下一个点
const char* pstr="hello world";
这里的pstr指针表示的不是指向hello world整个字符串,而是指向首字符h的地址,究其原因还是因为它是char指针,步长为一个字节,也就只能一个字符一个字符的指,若存在String指针,你要说它指向hello world那就应该没问题,但这里两个指针表示的地址是相同的,只是他们步长不同,所包围笼罩的范围不同;上述两个指针都加整数1,char跳过一个字符(字节),String跳过"hello world"对应的12个字节(末尾省略了\0)
用一道有趣的面试题来巩固一下:
#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; }
这里答案是什么呢?我们运行一下来看看,实践出真知:
1)str1与str2数组不同,并且数组是可变参数,所开辟的空间不同,数组名所表示的首元素地址也就不同;
2)str3与str4指向的是一个常量字符串,而且指向的是同一个常量字符串,原因是常量字符串不可更改,所以在空间中也没有创建两个的必要性,所以常量字符串在空间中是惟一的,str3、str4都指向同字符串的首字符地址,所以相同;
这里关于字符指针就不多赘述了;
3.数组指针与指针数组
哈哈,终于来这个头疼的一对兄弟了,这里是我初学时候的噩梦呀,见一次,查一次哈哈,这里我会用自己总结的方法来讲解,希望对大家有帮助!
- 再讲数组指针和指针数组之前,我们回顾一下操作符的优先级,这里我们就重点回顾
“*”与“【】”优先级,我们了解到“【】”优先级是大于 “星号 ”的,这里就先铺垫一下;
3.1指针数组
- 指针数组:我们从字面理解,就是装着指针的数组嘛,本质是数组,只不过是比较特殊的数组罢了,里面的元素都是指针;
- 若元素为一级指针,那便是一级指针数组 int* arr[5]------>前面int*就是arr数组元素类型;
- 那二级指针数组怎么表示呀,哈哈,就是int** arr[5]呗,是不是明了一些了呢。
- 注: 这里我是用int指针为例的,若数组元素为char那便前面也用char* arr[]即可;
- 这里也注意下:int* arr[10],[10]优先级高于星号,所以先和arr结合,也就是说明它是数组;
3.2数组指针
- 数组指针:也就是指向一个数组的指针,本质是指针,只不过指向的对象是数组而已;
- 这个就讲一下表达形式: int(*arr)[5] ,这里arr先和星号结合,所以它是指针;
- 对于上述的例子呢,它表达的含义就是一个指向含有五个整形元素 的 数组首元素地址的指针
大家在这里一定注意区分,arr先和谁结合,就相应对应什么类型,先和星号结合那么就是指针,先和中括号结合,那么就是数组;这里就先简单介绍到这,其他的会在后面一步步巩固。
3.3 &数组名与数组名
我们直到数组名代表着数组首元素的地址,但&数组名又代表什么呢,先不慌,我们来看一个代码:
#include <stdio.h> int main() { int arr[10] = {0}; printf("%p\n", arr); printf("%p\n", &arr); return 0; }
这里我们发现两者表示的地址相同,难道它们没有区别吗?我们接着测试:
#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+1步长为一个整形(4个字节),&arr+1步长为一个数组长度(40字节:这里是十个整形组成的数组);
- 于是,我们得出结论:&数组名表示的是该数组的首地址,数组名表示的是该数组的首元素的地址;
4.数组参数和指针参数
4.1 一维数组传参
int main() { int arr[10] = { 0 }; int* arr2[20] = { 0 }; test(arr); test2(arr2); return 0; }
如上面的代码,我们在使用函数的时候该如何接收呢?
这里我们列出一系列的传参方式,我来为大家分析是否可行:
//arr[10]传参: void test(int arr[]){} //这种方案是可行的,只是传入的数组大小不够明确,所以不建议这样 void test(int arr[10]){} //此方案补足了上一种方案的不足,原封不动的传参当然可以 void test(int *arr){} //这里是指针传参,arr本质为数组首元素地址,类型为int* 所以这里也没有问题 //arr2[20]传参: void test2(int *arr[20]){} //直接把原来的一维数组照搬,肯定没问题; void test2(int **arr){} //arr2的类型是int** 所以也没有问题
这里可以留意下,函数传参位置也就是上述的void test()括号内,必须保证类型一样即可,不必要求参数名称一致;
4.2 二维数组传参
//arr[3][5]={0}; void test(int arr[3][5]){} //直接照搬,毫无问题; void test(int arr[][]){} //这里就会出现问题了,对于二维数组我们在定义的时候就明确表示,二维数组行可以省略,列不可以省略;这里形参部分其实也相当于一个创建局部变量的问题,所以这里也无法成立; void test(int arr[][5]){} //这个形参满足类型要求,也满足二维数组创建要求,所以可以; //test(arr); void test(int *arr){} //arr是一级指针,但是二维数组数组名表示的是第一行开头的地址,也就是说它的元素是第一行,而int* arr表示的步长为int,元素也就是int类型,所以类型不符 void test(int* arr[5]){} //这是个指针数组,类型完全不符合; void test(int (*arr)[5]){} //数组指针,指向每行有五个元素的数组的指针,诶,这不就刚好和我们上面说的一致了吗?所以可行 void test(int **arr){} //二维数组的数组名是一级指针不能用二级指针接收 int main() { int arr[3][5] = {0}; test(arr); }
4.3 一级指针和二级指针传参
这个就不多赘述了,简单总结下:传入什么类型的指针,就用相同类型指针接收,但要注意上述数组传参中用指针接收,要注意类型;
5.函数指针
5.1 函数指针的定义
- 顾名思义,函数指针也就是指向一个函数首地址的指针,int(*func)(int);
- 表达式:(函数返回类型)(*函数名)(函数传入参数)
- 在我看来函数指针用途不大,反而是函数指针数组用途比较大,所以接下来就直接讲函数指针数组;
5.2 函数指针数组的用途
- 函数指针数组:int(*parr1[10])(int);我们一起来解读一下:parr1先和【10】结合,所以它是数组,然后把parr1[10]抹去,还剩下int(星号)(int)这个不就是函数指针吗,所以整体就是一个函数指针数组;函数指针数组用途:转移表
举个例子:
#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; }
如果用函数指针数组呢:
#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[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)) { printf( "输入操作数:" ); scanf( "%d %d", &x, &y); ret = (*p[input])(x, y); } else printf( "输入有误\n" ); printf( "ret = %d\n", ret); } return 0;
从上面这个简单的例子,我们不难看出函数指针数组在多个函数类同,作用方式相似的案例中,可以非常有效的缩短代码长度,起到简化的效果;
尘世熙熙攘攘,希望大家可以守护自己心中的一亩方田!获得片刻宁静~一起加油!