【C语言进阶】二、指针
一、指针数组
存放指针的数组
int* arr[10];
二、数组指针
指向数组的指针
int (*p)[10] 其中p是数组指针的名字
int (*) [10] 这个是类型(&arr 取出的类型)
三、数组参数和指针参数
一维数组传参,直接使用指针接收
void test(int *arr)
二维数组的传参,
void test(int arr[3][5]);
void test(int arr[][5]);//行可以省略,列不可以省略,但是不可以都省略
void test(int (*p)[5]);//形参写成指针的形式
注:
当函数的形参是二级指针时,不可以只把二维数组的数组名传入函数,因为二维数组的数组名是第一行的地址,需要用数组指针来接收。
四、函数指针
函数指针存储的是函数的地址,其中,例如,函数名是Add ,&Add 与 Add 意义一样;
int Add (int x,int y)
{
return x+y;
}
此函数的地址表达方式是: int (*pf)(int , int)=Add; pf 就是函数指针变量。
注:
a、int ret = (*pf) (2,3); 和 int ret = Add(2,3); 和 int ret = pf(2,3); 等价,其中第一个表达式中的 * 相当于摆设,无实际作用,但如果加了 * 千万不要忘了加括号,必须加括号!
例题:
下边的两行代码的意思
1 . ( *(void (*) () ) 0 ) ();
a、(void (*) () )是函数指针类型,是强制类型转换的作用;
b、(void (*) () ) 0 对0进行强制类型转换,意思就是认为0地址处放置着(void (*) () )类型的函数;
c、*(void (*) () ) 0 对此函数进行解引用
d、( *(void (*) () ) 0 ) () 调用该函数
2 . void(*signal (int , void ( * ) (int)) ) (int);
a、signal 先和括号结合,所以signal目前是一个函数
b、signal (int , void ( * ) (int)) 表示signal的第一个参数是 int ,第二个参数是void ( * ) (int) 是一个函数指针类型;
c、void(*signal (int , void ( * ) (int)) ) (int) 表示函数的返回类型是void(*) (int)
五、函数指针数组
例如有四个函数:
int (*pf1) (int ,int) = Add;
int (*pf1) (int ,int) = Sub;
int (*pf1) (int ,int) = Mul;
int (*pf1) (int ,int) = Div;
函数指针数组的表达方式
int (*pf[5]) (int ,int) = {0, Add, Sub, Mul, Div}; pf首先和 [4] 结合
六、指向函数指针数组的指针
函数指针 int (*pf) (int ,int ) = &Add;
函数指针数组 int (* pfarr[4]) (int , int);
指向函数中指针数组的指针 int (* (*p3)[4]) (int , int) = &pfarr;
七、回调函数
void test()
{printf("hehe\n");
}void print_hehe(void (*p) ())
{if(1){p();}
}int main()
{print_hehe(test);return 0;
}
以上代码,没有调用函数 test(),将函数指针传给了 print_hehe 由此函数通过 if 条件和函数指针来调用函数,此时 test() 是回调函数。
用法:
#include <stdio.h>void menu()
{printf("##############################\n");printf("##### 1.Add 2.Sub ####\n");printf("##### 3.Mul 4.Div ####\n");printf("##### 0.exit ####\n");printf("##############################\n");
}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 calc(int(*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入2个操作数:>");scanf("%d%d", &x, &y);ret = pf(x, y);printf("ret=%d\n", ret);
}int main()
{int input = 0;int ret = 0;do{menu();printf("请选择:>");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("退出!");break;default:printf("选择错误!");break;}} while (input);return 0;
}
上边代码中 calc() 就是回调函数.
下边是 qsort 的使用,qsort是一个库函数,是基于快速排序算法实现的一个排序函数,qsort可以排序任意类型的数据。
void qsort (void* base, 待排序数组的起始位置
size_t num, 数组元素个数
size_t width, 一个元素的字节大小
int (*cmp)(const void* e1,const void* e2) 函数指针,cmp是比较函数,e1、 e2是待比较两个元素的地址。返回值是当e1<e2返回<0的数;当e1=e2,返回0;当e1>e2时,返回>0的数。cmp是自定义的比较函数。
)
#include <stdio.h>
#include <stdlib.h>int cmp_int(const void* e1, const void* e2)
{return (*(int*)e1 - *(int*)e2);
}struct Stu
{char name[20];int age;double score;
};int cmp_stu_by_age(const void* e1,const void* e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}void test1()
{struct Stu arr[3] = { {"mike",20,55.5},{"lisa",30,88.0},{"kuka",50,90.0} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}void test()
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);
}int main()
{test();test1();return 0;
}
注:
a、void* 类型的指针可以接收任意类型的指针;
指针与数组相关例题解析
例1、
int main()
{int a[] = {1,2,3,4};printf("%d\n",sizeof(a))printf("%d\n",sizeof(a+0));printf("%d\n",sizeof(*a));printf("%d\n",sizeof(a+1));printf("%d\n",sizeof(a[1]));printf("%d\n",sizeof(&a));printf("%d\n",sizeof(*&a));printf("%d\n",sizeof(&a+1));printf("%d\n",sizeof(&a[0]));printf("%d\n",sizeof(&a[0]+1));return 0;
}
sizeof 计算对象所占内存的大小,单位是字节。
printf("%d\n",sizeof(a)) 16
printf("%d\n",sizeof(a+0)); 4/8 表示数组首元素的地址
printf("%d\n",sizeof(*a)); 4 表示第一个元素的大小
printf("%d\n",sizeof(a+1)); 4/8 第二个元素的地址的大小
printf("%d\n",sizeof(a[1])); 4 第二个元素的大小
printf("%d\n",sizeof(&a)); 4/8 数组的地址的大小
printf("%d\n",sizeof(*&a)); 16 计算整个数组的大小
printf("%d\n",sizeof(&a+1)); 4/8 计算指向数组后边数组处地址的大小
printf("%d\n",sizeof(&a[0])); 4/8 第一个元素地址的大小
printf("%d\n",sizeof(&a[0]+1)); 4/8 第二个元素地址的大小
例2、
int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", sizeof(arr));printf("%d\n", sizeof(arr+0));printf("%d\n", sizeof(*arr));printf("%d\n", sizeof(arr[1]));printf("%d\n", sizeof(&arr));printf("%d\n", sizeof(&arr+1));printf("%d\n", sizeof(&arr[0]+1));printf("%d\n", strlen(arr));printf("%d\n", strlen(arr+0));printf("%d\n", strlen(*arr));printf("%d\n", strlen(arr[1]));printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr+1));printf("%d\n", strlen(&arr[0]+1));return 0;
}
sizeof 计算对象所占内存的大小,单位是字节。
printf("%d\n", sizeof(arr)); 6
printf("%d\n", sizeof(arr+0)); 4/8
printf("%d\n", sizeof(*arr)); 1
printf("%d\n", sizeof(arr[1])); 1
printf("%d\n", sizeof(&arr)); 4/8
printf("%d\n", sizeof(&arr+1)); 4/8
printf("%d\n", sizeof(&arr[0]+1)); 4/8printf("%d\n", strlen(arr)); 随机值
printf("%d\n", strlen(arr+0)); 随机值
printf("%d\n", strlen(*arr)); err,此时*arr是首元素,首元素的值是97,可能会发生越界
printf("%d\n", strlen(arr[1])); err
printf("%d\n", strlen(&arr)); 随机值
printf("%d\n", strlen(&arr+1)); 随机值
printf("%d\n", strlen(&arr[0]+1)); 随机值
例3、
int main()
{char arr[] = "abcdef";printf("%d\n", sizeof(arr));printf("%d\n", sizeof(arr+0));printf("%d\n", sizeof(*arr));printf("%d\n", sizeof(arr[1]));printf("%d\n", sizeof(&arr));printf("%d\n", sizeof(&arr+1));printf("%d\n", sizeof(&arr[0]+1));printf("%d\n", strlen(arr));printf("%d\n", strlen(arr+0));printf("%d\n", strlen(*arr));printf("%d\n", strlen(arr[1]));printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr+1));printf("%d\n", strlen(&arr[0]+1));return 0;
}
sizeof 计算对象所占内存的大小,单位是字节。
printf("%d\n", sizeof(arr)); 7
printf("%d\n", sizeof(arr+0)); 4/8 数组首元素地址
printf("%d\n", sizeof(*arr)); 1 首元素
printf("%d\n", sizeof(arr[1])); 1
printf("%d\n", sizeof(&arr)); 4/8
printf("%d\n", sizeof(&arr+1)); 4/8
printf("%d\n", sizeof(&arr[0]+1)); 4/8printf("%d\n", strlen(arr)); 6
printf("%d\n", strlen(arr+0)); 6
printf("%d\n", strlen(*arr)); err
printf("%d\n", strlen(arr[1])); err
printf("%d\n", strlen(&arr)); 6
printf("%d\n", strlen(&arr+1)); 随机值
printf("%d\n", strlen(&arr[0]+1)); 5
例4、
int main()
{char *p = "abcdef";printf("%d\n", sizeof(p));printf("%d\n", sizeof(p+1));printf("%d\n", sizeof(*p));printf("%d\n", sizeof(p[0]));printf("%d\n", sizeof(&p));printf("%d\n", sizeof(&p+1));printf("%d\n", sizeof(&p[0]+1));printf("%d\n", strlen(p));printf("%d\n", strlen(p+1));printf("%d\n", strlen(*p));printf("%d\n", strlen(p[0]));printf("%d\n", strlen(&p));printf("%d\n", strlen(&p+1));printf("%d\n", strlen(&p[0]+1));return 0;
}
sizeof 计算对象所占内存的大小,单位是字节。
printf("%d\n", sizeof(p)); 4/8
printf("%d\n", sizeof(p+1)); 4/8
printf("%d\n", sizeof(*p)); 1
printf("%d\n", sizeof(p[0])); 1
printf("%d\n", sizeof(&p)); 4/8
printf("%d\n", sizeof(&p+1)); 4/8
printf("%d\n", sizeof(&p[0]+1)); 4/8
printf("%d\n", strlen(p)); 6
printf("%d\n", strlen(p+1)); 5
printf("%d\n", strlen(*p)); err
printf("%d\n", strlen(p[0])); err
printf("%d\n", strlen(&p)); 随机值
printf("%d\n", strlen(&p+1)); 随机值
printf("%d\n", strlen(&p[0]+1)); 5
例5、
int main()
{inta[3][4] = {0};printf("%d\n",sizeof(a));printf("%d\n",sizeof(a[0][0]));printf("%d\n",sizeof(a[0]));printf("%d\n",sizeof(a[0]+1));printf("%d\n",sizeof(*(a[0]+1)));printf("%d\n",sizeof(a+1));printf("%d\n",sizeof(*(a+1)));printf("%d\n",sizeof(&a[0]+1));printf("%d\n",sizeof(*(&a[0]+1)));printf("%d\n",sizeof(*a));printf("%d\n",sizeof(a[3]));return 0;
}
sizeof 计算对象所占内存的大小,单位是字节。
printf("%d\n",sizeof(a)); 48
printf("%d\n",sizeof(a[0][0])); 4
printf("%d\n",sizeof(a[0])); 16 第一行的大小
printf("%d\n",sizeof(a[0]+1)); 4/8 第一行首元素的地址+1是第一行第二个元素的地址
printf("%d\n",sizeof(*(a[0]+1))); 4 第一行第二个元素
printf("%d\n",sizeof(a+1)); 4/8 a表示二维数组第一行的地址,a+1是第二行的地址
printf("%d\n",sizeof(*(a+1))); 16 对第二行的地址解引用
printf("%d\n",sizeof(&a[0]+1)); 4/8 第二行的地址
printf("%d\n",sizeof(*(&a[0]+1))); 16 第二行的大小printf("%d\n",sizeof(*a)); 16 第一行的大小
printf("%d\n",sizeof(a[3])); 16 第四行的大小,此时虽然越界,但没有访问,所以可以正常运行。
例6、
int main()
{int a[5] = { 1, 2, 3, 4, 5 };int*ptr= (int*)(&a+1);printf( "%d,%d", *(a+1), *(ptr-1)); return0;
}
结果是2、5
例7、
struct Test
{int Num;char*pcName;short sDate;char cha[2];short sBa[4];
}*p;int main()
{p=0x100000;printf("%p\n", p+0x1);printf("%p\n", (unsignedlong)p+0x1);printf("%p\n", (unsignedint*)p+0x1);return0;
}
答案是:
00100014 当结构体指针+1时,加的是结构体的大小,所以加20,又因为是16进制表示
00100001 指针变量p被转换成无符号长整型变量,所以+1是就是数值直接+1
00100004 指针变量p被转换成整形指针,+1 就是加一个整形的大小
例8、
int main()
{int a[4] = { 1, 2, 3, 4 };int* ptr1= (int*)(&a+1);int* ptr2= (int*)((int)a+1);printf( "%x,%x", ptr1[-1], *ptr2); return 0;
}
答案 0x4 0x20000000
例9、
#include <stdio.h>
int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };int* p;p = a[0];printf( "%d", p[0]);return 0;
}
答案是:1;因为花括号中的小括号相当于逗号表达式,所以数组 a 的初始化相当于,a[3][2]={1,3,5};而 a[0]是第一行的地址,打印时,打印第一行的首元素。
例10、
int main()
{int a[5][5];int(*p)[4];p=a;printf( "%p,%d\n", &p[4][2] -&a[4][2], &p[4][2] -&a[4][2]);return0;
}
答案: FFFFFFFC, -4
p[4][2] 等价于 *(*(p+4)+2) ; p 最初是数组名,也就是数组首元素的地址,p是一个数组指针,p+1相当于,向后跳过四个元素,而指针-指针得到的是指针之间的元素个数。自行画图
例11、
int main()
{int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int*ptr1= (int*)(&aa+1);int*ptr2= (int*)(*(aa+1));printf( "%d,%d", *(ptr1-1), *(ptr2-1));return 0;
}
答案:10 5
例12、
int main()
{char*a[] = {"work","at","alibaba"};char**pa=a;pa++;printf("%s\n", *pa);return 0;
}
答案:at
因为 char *a[] 中存的是这三个字符串的首字符的地址;
char** pa 中存的是 a 的地址;
pa 最初是指向 a 数组的第一个元素,即 "work" 中w的地址;
pa++ 后指向了 a 数组中第二个元素,即 "at" 中 a 的地址;
又因为数组中每个字符串最后都有一个 \n 所以最后打印 at。
例13、
int main()
{char*c[] = {"ENTER","NEW","POINT","FIRST"};char**cp[] = {c+3,c+2,c+1,c};char***cpp=cp;printf("%s\n", **++cpp);printf("%s\n", *--*++cpp+3);printf("%s\n", *cpp[-2]+3);printf("%s\n", cpp[-1][-1]+1);return0 ;
}
答案: POINT ER ST EW
*cpp[-2] 等价于 **(cpp-2)+3;
cpp[-1][-1] 等价于 *(*(cpp-1)-1)+1`;
C8 39
【C语言进阶】二、指针相关推荐
- C语言进阶——函数指针
作者:敲代码の流川枫 博客主页:流川枫的博客 专栏:C语言从入门到进阶 语录:Stay hungry stay foolish 工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器--牛客 ...
- 【C语言进阶】指针(进阶2)
目录 回顾: 数组参数.指针参数 一维数组传参 二维参数传参 一级指针传参 二级指针传参 回顾: 前面关于指针的内容我们已经学习了指针数组和数组指针我们来一起看看下面的代码的意思 int arr[5] ...
- 【C语言-进阶】指针进阶(2)
继上一篇的数组指针,简单描述:数组指针就是指向数组的指针,二维数组传参时,传递的是第一个一维数组的地址,所以可以用指向一维数组的指针来接收. 1:整型数组 2:整型指针的数组 3.数组指针,所指向的数 ...
- 【C语言进阶】指针 下
目录 1.回调函数 1.1.回调函数的概念 1.2.回调函数的使用 1.2.1. 案例一: 1.2.2.案例二: 1.3.qsort函数 1.3.1.qsort函数介绍 1.3.2. qsort函 ...
- 【C语言进阶】指针编程题—字符串翻转
目录 写在前面 正文 练习一:左旋字符串 练习二:字符串旋转异同判断 练习三:杨氏矩阵 写在最后 写在前面 这是有关指针的大题 正文 练习一:左旋字符串 题目要求:实现一个函数,可以左旋字符串中的K个 ...
- 【C语言进阶深度学习记录】二十六 C语言中的字符串与字符数组的详细分析
之前有一篇文章是学习了字符和字符串的,可以与之结合学习:[C语言进阶深度学习记录]十二 C语言中的:字符和字符串 文章目录 1 字符串的概念 1.1 字符串与字符数组 1.2 字符数组与字符串代码分析 ...
- c语言野指针导致问题,C语言进阶之路(三)----野指针的产生原因及解决办法
1.会产生野指针的做法 #include //这就是一种错误的写法 int main(){ int *p = NULL; p = (int *)malloc(); //释放P所指向的内存空间,但指针变 ...
- c语言未初始化的指针下标访问是0,C语言的二数组的指针访问.doc
C语言的二数组的指针访问 二维数组的指针访问 --王炳华 指向二维数组的指针及用指针访问二维数组,是学习指针的最大难点.如果真正弄懂了这个问题,就可以说你学会了使用指针. 二维数组的指针 指针就是地址 ...
- C语言进阶-高阶指针
目录 前言 字符指针 指针数组 数组指针 &数组名VS数组名 数组指针的使用 数组参数.指针参数 一维数组传参 二维数组传参 一级指针传参 二级指针传参 函数指针 函数指针数组 指向函数指针数 ...
- C语言进阶——地址和指针
一.地址 1.什么是地址 再计算机运行时,数据会存放在内存中,内存会以字节为单位划分为多个存储空间,并且为每个字节默认设置一个对应的编号,这个编号就是地址 地址只是计算机规定的一个值,所以不会占用内存 ...
最新文章
- linux字符终端看视频,在Linux终端上看电影很酷吗?
- SFB 项目经验-51-某上市企业2千人Exchange 2013升级2016高可用之伤01
- 2.1 Mini-batch 梯度下降-深度学习第二课《改善深层神经网络》-Stanford吴恩达教授
- BASIC-1_蓝桥杯_闰年判断
- [导入] 堆和栈的区别
- 学习笔记(13):Python网络编程并发编程-解决粘包问题-终极版本
- 遍历某个文件夹下的所有文件并格式化显示出来
- js获取当前页面url网址等信息
- 如何以及为什么使用Spoon分析,生成和转换Java代码
- LeetCode 1199. 建造街区的最短时间(优先队列贪心)
- 求职,北京,.netGIS
- Java NIO学习篇之通道Channel详解
- 视频转音频时,安卓和iOS播放的时长翻倍 --- 好一个坑
- docker中java应用new FileOutputStream直接报Input/output error
- 批量ssh免密登陆远程主机
- java 创建txt_java创建txt文件并存入内容
- java Date时间工具类
- STATA 学习笔记: outlier(离群值)的处理
- 浅谈Android指纹识别技术
- latex 参考文献没有显示_LaTeX 参考文献的处理