青建设厅官方网站海省,换域名影响网站不,云南省建设厅合同网站,成都做网站开发的公司在之前的文章中#xff0c;我已经讲解过了初阶指针的内容#xff0c;今天就来讲一讲指针的进阶#xff01;
上篇指针地址#xff1a;保姆式指针讲解#xff0c;超详细#xff0c;适合初学者_指针详解_陈大大陈的博客-CSDN博客
目录 1. 字符指针
2. 指针数组
3. 数组指…在之前的文章中我已经讲解过了初阶指针的内容今天就来讲一讲指针的进阶
上篇指针地址保姆式指针讲解超详细适合初学者_指针详解_陈大大陈的博客-CSDN博客
目录 1. 字符指针
2. 指针数组
3. 数组指针 3.1 数组指针的定义
3.2 数组名和数组名的关系
3.3 数组指针的使用
4. 数组参数、指针参数
4.1 一维数组传参
4.2 二维数组传参 4.3 一级指针传参
4.4 二级指针传参 5. 函数指针
6. 函数指针数组 博客昵称陈大大陈 座右铭所谓觉悟就是在漆黑的荒野上开辟出一条理当前进的光明大道。 博主简介一名热爱C/C和算法等技术喜欢运动爱胡思乱想却胸怀大志的小博主 博主唠嗑早中晚安各位CSDN的朋友我是博客新人陈大大陈希望我的文章能为你带来帮助欢迎大家在评论区畅所欲言也希望大家多多为我提出您宝贵的建议如果觉得我写的不错的话还请点个赞和关注哦~ 正文开始前我们先来复习一下指针的概念。 指针就是一个变量用来存储地址地址属于唯一的一片内存空间。在32位平台下指针的大小是4字节在64位平台下则是8字节。指针是有类型的指针的类型决定了指针运算的步长即与整数相加跳过几个元素指针的类型还决定了指针解引用操作时候的权限。1. 字符指针
在指针的类型中字符类型的指针为char *。
#includestdio.h
int main()
{char ch w;char* pc ch;*pc w;return 0;
}
还可以这样用。
#includestdio.h
int main()
{const char* pstr hello world.;printf(%s\n, pstr);return 0;
}
这里是把一个字符串放到pstr指针变量里了吗
答案是否定的 实际情况如图所示。 const char* pstr hello world.;这句话本质的的意思是将hello world.字符串的首元素的地址存储到pstr里面通过解引用操作符即可将首字母释放出来。 我一开始还以为这句话是把字符串 hello world 放到字符指针 pstr 里了大家不要像我一样踩雷哦。
上面代码的意思是把一个常量字符串的首字符 h 的地址存放到指针变量 pstr 中。 有这样的面试题
#include stdio.h
int main()
{char str1[] hello world.;char str2[] hello world.;const char *str3 hello world.;const char *str4 hello world.;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 not same\n);return 0;
}
运行结果 为什么会这样呢
因为在str3和str4都指向一个字符串的情况下C/C会把字符串存储到单独的一个内存区域也就是说当多个指针指向同一个常量字符串它们都会指向同一块内存空间。
但是用相同的常量字符串去初始化 不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同str3和str4不同。
2. 指针数组
指针数组是一个存放指针的数组。
这里我们再复习一下下面指针数组是什么意思 int* arr1[10]; //整形指针的数组 char *arr2[4]; //一级字符指针的数组 char **arr3[5];//二级字符指针的数组 一级指针 我们把内存单元当作一个房间房间里的东西就相当于是内存单元中所存放的内容指针就可以看作是这个房间的门牌号门牌号就相当于地址给你了门牌号你就可以通过门牌号找到这个房间了。也就是上面所说的通过指针所指向的地址找到所指向内存单元的内容。
二级指针 也不难理解通过一级指针我们可以得到这个变量的地址那么二级指针就可以得到这个变量地址的地址。这里看下面的调试中代码理解一下 形象化的话就是下图这样 3. 数组指针 3.1 数组指针的定义
数组指针到底是指针还是数组呢
答案是指针。
整形指针 int * p; 能够指向整形数据的指针。
浮点型指针 float * pf; 能够指向浮点型数据的指针。
比葫芦画瓢数组指针就是能够指向数组的指针。
下面代码哪个是数组指针 int *p1[10]; int (*p2)[10]; //p1, p2分别是什么 答案是int (*p)[10],因为p先和*结合表示p是一个指针变量后面的[10]代表指向一个大小为十个整形的数组。
这里要注意[]的优先级要高于*号的所以必须加上来保证p先和*结合。
3.2 数组名和数组名的关系 int a[10] 对于上面的数组a和a分别代表什么呢又有什么区别呢
我们知道a是数组名数组名代表首元素的地址。
那么a究竟代表什么呢
我们看一段代码
#includestdio.h
int main()
{int a[10] { 0 };printf(%p\n, a);printf(%p, a);return 0;
} 运行结果是这样的 可见数组名和数组名打印的地址是一样的。
难道两个是一样的吗
我们再看一段代码
#include stdio.h
int main()
{int a[10] { 0 };printf(a %p\n, a);printf(a %p\n, a);printf(a1 %p\n, a1);printf(a1 %p\n, a1);return 0;
}运行结果如下 根据上面的代码我们发现a和a虽然值是一样的但是意义是不一样的。 a表示的是数组的地址而不是首元素的地址。
本例中 a 的类型是 int(*)[10] 是一种数组指针类型。
数组的地址1跳过整个数组的大小我们经过计算发现 a1 相对于 a 的差值是40即十个整形的大小说明确实跳过了整个数组。
3.3 数组指针的使用
那数组指针是怎么使用的呢 既然数组指针指向的是数组那数组指针中存放的应该是数组的地址。
#include stdio.hint main(){int arr[10] { 1,2,3,4,5,6,7,8,9,0 };int(*p)[10] arr;//把数组arr的地址赋值给数组指针变量p//但是我们一般很少这样写代码return 0;
}一个数组指针的使用
#define _CRT_SECURE_NO_WARNINGS
#include stdio.h
void print_arr1(int arr[3][5], int row, int col)
{int i 0,j 0;for (i 0; i row-1; i){for (j 0; j col; j){printf(%d , arr[i][j]);}printf(\n);}
}
void print_arr2(int(*arr)[5], int row, int col)
{int i 0,j 0;for (i 0; i row-1; i){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_arr1(arr, 3, 5);//数组名arr表示首元素的地址//但是二维数组的首元素是二维数组的第一行//所以这里传递的arr其实相当于第一行的地址是一维数组的地址//可以数组指针来接收print_arr2(arr, 3, 5);return 0;
}
运行结果如下 两种打印方式均可打印出数据。
我们再来看下面的代码。 int (*parr3[10])[5] 该如何理解这个数组指针呢请看下面我作的图。 parr3是数组该数组存放的是指针指针指向的元素又是数组。
4. 数组参数、指针参数
在写代码的时候难免要把数组或者指针传给函数那函数的参数该如何设计呢
4.1 一维数组传参
#define _CRT_SECURE_NO_WARNINGS
#include stdio.h
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);
} 经过逐一测试以上均可编译成功。
一维数组进行传参的时函数参数可以写成数组或指针因为数组名是地址可用指针接收。
参数是指针时要找到对应的指针类型才行。
4.2 二维数组传参
#define _CRT_SECURE_NO_WARNINGS
#include stdio.h
void test(int arr[3][5])//ok1
{}
void test(int arr[][])//ok2
{}
void test(int arr[][5])//ok3
{}
void test(int* arr)//ok4
{}
void test(int* arr[5])//ok5
{}
void test(int(*arr)[5])//ok6
{}
void test(int** arr)//ok7
{}
int main()
{int arr[3][5] { 0 };test(arr);
}二维数组传参函数形参的经过测试只有1,3,5可以完成二维数组传参的任务。
总结 设计只能省略第一个[]的数字。 因为对一个二维数组可以不知道有多少行但是必须知道一行多少元素。 这样才方便运算。 2省略了第二个[]的数字4是一维数组传参5是一个一维指针数组7代表将一个指针数组解引用所以它们不能编译。 4.3 一级指针传参
#include stdio.h
void print(int *p, int sz)
{int i 0;for(i0; isz; i){printf(%d\n, *(pi));}
}
int main()
{int arr[10] {1,2,3,4,5,6,7,8,9};int *p arr;int sz sizeof(arr)/sizeof(arr[0]);//一级指针p传给函数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. 函数指针
看下面的代码
#define _CRT_SECURE_NO_WARNINGS
#include stdio.h
void test()
{printf(hehe\n);
}
int main()
{printf(%p\n, test);printf(%p\n, test);return 0;
}运行结果如下 输出的是两个地址这两个地址是 test 函数的地址。 我们可以存储各种类型数据的地址那么该怎么存储函数的地址呢
请看代码
#include stdio.h
void test()
{printf(hehe\n);
}
//下面pfun1和pfun2哪个有能力存放test函数的地址
void (*pfun1)();
void* pfun2();经过测试void (*pfun1)()可以存放数组的地址。在里*先与pfun1结合表示*pfun1是一个指针它指向的类型是函数指向的函数无参数而返回类型是void。 阅读下段出自《C陷阱和缺陷》的有趣代码 (*(void (*)())0)() 这种代码就是冯诺依曼来了都得楞两秒那么该怎么去理解这个代码呢 将0强制类型转换为void*类型的函数指针然后通过*和调用。这就意味着在0的地址处放着一个函数。函数的返回类型是void没有参数。再来看这个代码 void (*signal(int , void(*)(int)))(int) 这种代码就是丹尼斯·里奇来了都得楞两秒那么该怎么去理解这个代码呢 上述的代码是一个函数声明。该函数的一个参数为int另一个参数是void(*)(int)类型函数指针。这个函数指针的参数是int返回类型是void。signal函数的返回类型也是一个函数指针。该函数指针指向的函数参数也是int返回类型是void。 这个代码可以用类型重命名操作符来简化。
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t); 这样看起来是不是整洁多了
6. 函数指针数组
数组是一个存放相同类型数据的存储空间那我们已经学习了指针数组。 int *arr[10]; 数组的每个元素是int* 函数指针是如何定义的呢如下所示。 int (*parr1[10])(); parr1 先和 [] 结合说明 parr1是数组。
数组的内容是什么呢 是 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);break;default:printf( 选择错误\n );break;}} while (input);return 0;
}但是这样的代码十分冗杂代码重复内容太多。
使用函数指针数组的实现就会简单很多
#define _CRT_SECURE_NO_WARNINGS
#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);}elseprintf(输入有误\n);printf(ret %d\n, ret);}return 0;
}本篇文章旨在分享C语言拔高知识——指针的进阶。希望我的文章能够让大家有所收获大佬们如果对我的文章有什么建议或者认为那里写的不好请在评论区写下您宝贵的意见如果觉得我写的不错的话还请点个赞和关注哦~我会持续输出编程的知识的