一、字符指针变量
(1)一般使用
(2)另一种使用
①思考
- (思考:是不是把字符串包括\0存储到指针pstr中)
②本质
-
把字符串 hello bit. ⾸字符的地址放到了pstr中。
③理解
- 可以把字符串想象为一个字符数组,把首元素的地址赋给了pstr。但是这个数组是不能修改的(因为这是一个“常量”字符串)
- 当常量字符串出现在表达式中的时候,他的值是第一个字符的地址
(3)示例
- 这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域,当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
二、数组指针变量
(1)概念
-
存放的是数组的地址,能够指向数组的指针变量。
(2)类型剖析
-
int (*parr)[10] (去掉名字,即指针的类型——int(*)[10])
(3)理解
![](https://img-blog.csdnimg.cn/direct/7bf26d6064a144188674082ae98b3bd4.png)
- p1是数组,数组有10个元素,所以p1是指针数组
- p2是指针,指针指向的数组,数组有10个元素,每个元素的类型是int,所以p2是数组指针
- []的优先级要⾼于*号的,所以必须加上()来保证p先和*结合。
(4)初始化
-
int(*p)[10] = &arr
三、二维数组传参本质
(1)类比理解
①一维数组传参
-
一维数组传参,形参既可以是数组,也可以是指针
-
写成数组更加直观,为了方便理解
-
写成指针是因为数组传参,传递的是数组第一个元素的地址
②二维数组传参
-
二维数组传参,形参写成数组也是可以的,非常直观,容易理解。
-
也可以写成指针,写成指针,数组名表示第一行数组的地址(首元素)
(2)深入理解二维数组
- ⼆维数组可以看做是每个元素是⼀维数组的数组,也就是⼆维数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。
-
根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀维数组的地址。
-
第⼀⾏的⼀维数组的类型就是 int [5] ,所以第⼀⾏的地址的类型就是数组指针类型 int(*)[5],⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏这个⼀维数组的地址
(3)二维数组传参也可以写成指针
- 以arr[3][5]为例,传参时写成 int (*arr)[5] (因为二维数组传参传递是一维数组的指针,所以要写成指向数组的指针)
- arr[i][j] === *(*(arr+i)+j)
四、函数指针变量
(1)函数指针变量
- 用来存放函数的地址
- 函数名:函数的地址
- &函数名:函数的地址
(2)函数指针变量的创建
- int(*pf)(int,int) = &add
- (*pf) ==> 指针
- (形参列表)==>表面是函数指针
- 函数返回值类型 (*指针名)(形参类型l列表) = 函数名/&函数名
(3)函数指针变量的使用
![](https://img-blog.csdnimg.cn/direct/b51ec62596174b429b412a070e68ffb9.png)
(4)两段有趣的代码解读
(*(void(*)())0)();
- void(*)()==> 函数指针类型 →加()---强制类型转换, 加(* )-->调用
- 1.把0这个整数,强制类型转换成一个函数的地址,这个函数没有参数,返回类型是void
- 2.去调用0地址的函数
void (*signal(int, void(*)(int)))(int);
-
signal是一个函数声明
-
signal函数的第一个参数的类型是int,第二个参数的类型是函数指针void(*)(int),这个函数指针指向的函数参数类型是int,返回类型是void
-
可以理解为下列形式:void(*)(int) signal(int, void(*)(int);
(5)typedef关键字
-
typedef 是⽤来类型重命名的,可以将复杂的类型,简单化。
五、函数指针数组
-
int (*parr[3])();
-
parr先和 [] 结合,说明 parr是数组,数组的内容是 int (*)() 类型的函数指针。