本节重点
- 字符指针
- 数组指针
- 指针数组
- 数组传参和指针传参
- 函数指针(难)
- 函数指针数组(难)
- 指向函数指针数组的指针
- 回调函数(难)
前言
在C语言基础阶段,我们学习过指针相关的一些基础内容,比如说:
在指针的类型中我们知道有一种指针类型为字符指针 char* ;
一般使用:
或
扩展:在C语言中,内存可以被划分为栈区、堆区、静态区、常量区。
- 栈区:局部变量,函数形参,函数调用
- 堆区:动态内存如malloc等申请使用
- 静态区:全局变量,static修饰的局部变量
- 常量区:常量字符串(常量区中的内容在整个程序的执行期间是不允许被修改的,且同一份常量字符串只会创建一份,不会重复创建存储。)
有这样的面试题:
打印出的结果:
(自我理解)
- str1与str2相当于先选两个位置(地址),再搭房子(存hello world),因此两房子不同(只是长得一样,不是同一个房子)
- 而str3与str4则相当于先造一个房子(存hello world),然后用两张纸写的同一个地址
int* arr1[10]; //整型指针的数组
char* ch[5]; //字符指针的数组
char* arr2[4]; // 一级字符指针的数组
char arr3[5]; //二级字符指针的数组(存放二级指针)
首先,我们要知道[]的优先级是比 * 要高的,对于形式1,pa会先与[ ]结合,再与 * 结合,所以形式1是指针数组
()的优先级又比[]高,所以pa会先于 * 结合,在与[ ]结合,所以形式2是数组指针。
<br>可以看出,arr+1 相较于 arr 跳过了4个字节的大小, <br>而 &arr+1 相较于 &arr 跳过了40(10*4)个字节,一个数组的地址 <br>因为&arr 指向的是一整个数组
大多数情况下数组名是数组首元素的地址,但是有两个例外:
- sizeof(数组名)
- &数组名
3.3.1 对一维数组的使用
例: 1
例: 2
可以看出对一维数组使用例1的方法更方便简捷,例2的方法有点鸡肋
3.3.2 对二维数组的使用
3.3.3 巩固练习
下面这些代码的含义是什么?
解析:
答案:以上传参方式相对应的均ok
注意:一维数组传参可以传数组形式,也可以传指针形式,传数组形式的时候数组元素的个数可以不写,也可以写,传指针的时候要注意指针的类型,也就是指针指向什么类型的元素,
比如说指针指向int类型元素,那么指针的类型就是 int*
总结 : 二维数组传参,函数形参的设计只能省略第一个[ ]的数字。
因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
这样才方便运算。
二维数组传参也能写成指针的形式,指针的类型应该是数组指针。 传什么,就用什么接收
注:数组类型参数 arr 1发生退化,变成了指向数组第一个元素的指针。这意味着在函数内部,arr 1被当作一个指针来处理,而不是一个完整的数组。
分下下面两段代码
代码1:
(void (*)())0 —— 把0强制转化为一个函数的地址
(*(void ( * )())0)();——解引用0地址的函数,并调用
代码2:
剩下void( * )( int )——说明signal的返回类型也是一个函数指针类型
上述代码是一次函数声明 // 声明的函数叫:signal // signal函数的第一个参数是int类型的 // signal函数的第二个参数是一个函数指针类型,该函数指针指向的函数参数是int,返回类型是void // signal函数的返回类型也是一个函数指针类型,该函数指针指向的函数参数是int,返回类型是void
// void (*)(int) signal(int, void( * )(int)); //err
上述简化是错的,可以用 typedef 函数进行简化
typedef void (*pf_t)(int) ;//pf_t为 void ( * )(int) 函数指针类型
pf_t signal( int, pf_t);
数组是一个存放相同类型数据的存储空间,我们已经学习了指针数组与函数指针,比如︰
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢 ?
int ( * parr1[10] )(int...... ); √
int* parr2[10] ( ); x
int (*)( ) parr3[10]; x
- parr1先和[结合,说明parr1是数组。
- 数组的内容是什么呢 ?
- 是int(*)()类型的函数指针。
改进
这种函数指针数组 我们经常称为 转移表
暂时能认识就行
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进 行响应。
每个case中语句执行重复度太高
改进
当calc函数传送的是Add函数的地址,这是p调用的是Add函数,这是我们称Add为回调函数
这里并不是直接调用各种计算函数,而是把计算函数函数的地址,传递给了calc函数
再由calc函数指针 int ( * p)(int, int) 之后,在适当位置通过函数指针 p 调用它所指向的函数
这个机制就称为回调函数
8.2.1 回顾冒泡排序
8.2.2 qsort介绍
void* 类型讲解: void是无,空的含义,void * 表示的是无类型的指针。 void* 指针可以接收任意类型的地址(void 可以看作指针万能筒,可以接收任意类型) void 类型指针不能进行解引用操作的。
理解:我们知道指针类型的意义除了决定指针解引用时访问字节的个数外,还决定了指针 + -整数运算的步长,如果指针类型是void * 无类型指针,那么就无法得知该指针 + -的步长到底是多少个字节。
8.2.3 qsort在升序降序中的使用
8.2.3 qsort在结构体排序中的使用
改造冒泡排序,使得其能排序任意指定数组
请写出下面程序执行的结果
解析:数组名表示首元素地址,但有两个例外:
1.sizeof(数组名)-- - 此时数组名表示整个数组。 2.& 数组名-- - 此时数组名表示整个数组。
注意:上面这两种例外的情况都需要 sizeof / &后面直接跟数组名,如果不是直接 + 数组名,则不是表示整数数组。
除了上面两个例外情况外,余下遇到的所有情况数组名均表示首元素地址
请写出下面程序执行的结果
详解及结果展示:
请写出下面程序执行的结果
strlen函数是求字符串长度,需要找到 ’/0 ’
详解及结果展示:
请写出下面程序执行的结果
sizeof只关注占用内存空间的大小,单位是字节,不关心内存中存放的是什么
sizeof是操作符
strlen是求字符串长度的,统计的是0出现前出现的字符个数,找到0结束,所以可能存在越界访问
strlen是库函数
详解及结果展示:
请写出下面程序执行的结果
详解及结果展示:
请写出下面程序执行的结果
详解及结果展示:
请写出下面程序执行的结果
详解及结果展示:
请写出下面程序执行的结果
知识储备: 二维数组的数组名表示首元素地址时,首元素指的是第一行数组,也就是说首元素是数组,数组名表示的是第一行数组的地址
详解及结果展示:
2,5
(int*)(&a+1),这里&a表示数组的地址,然后+1是跳过整个数组大小,
*(a+1)表示首元素地址+1,就是a[2]的地址,然后解引用,就打印2
*(ptr - 1) 表示在跳过一个数组地址后再-1;就得到了没有跳过之前数组的最后一个元素的地址,再进行解引用,就得到了5
详解及结果展示:
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/rfx/69332.html