1.字符指针变量
字符指针变量,char*,一般使用方式:
char ch = 'w';
char *pc = &ch;
*pc = w;
还有一种使用方式:
const char* pstr = 'hello world.';
printf("&s\n",pstr);
这里并不是把字符串hello world整个放入了字符指针pstr中,而是只放入了首字符h的地址到pstr中。
我们通过《剑指offer》中的一道题来加深对字符指针的理解:
#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");//1
else
printf("str1 and str2 are not same\n");//2
if(str3 ==str4)
printf("str3 and str4 are same\n");//3
else
printf("str3 and str4 are not same\n");//4
return 0;
}
这里打印的结果是 2和3两句话。
对于str1和str2来说,它们存储的内容虽然是一样的,但是是储存在不同的内存单元;而对于str3和str4来说,它们指向的是同一个常量字符串,当几个指针指向同一个字符串的时候,它们实际上会指向同一块内存。但是用相同的常量字符串去初始化不同的数组时会开辟出不同的内存块,所以str1和str2不同,而str3和str4相同。
2.数组指针变量
2.1 数组指针变量是什么
数组指针变量是一种变量,是一种指针变量,指向的对象是数组。
int (*p) [10];//数组指针变量
解释:p先和*结合,说明p是一个指针变量,指针指向的是一个大小为10个整形的数组。所以p是一个指针,指向一个数组,叫数组指针。
注意:[]的优先级高于*号,必须加上()来保证p和*先结合。
2.2 数组指针变量怎么初始化
数组指针变量是用来存放数组的地址的,如果要存放某个数组的地址,就得存放在数组指针变量中,用&数组名可以获得数组的地址。
int (*p) [10] = &arr;
可以使用调试看出&arr和p的类型是完全一致的。
3.二维数组传参的本质
通常情况下,想将一个二维数组传参给函数作为参数,是这样书写的:
void test(int a[3][5],int r,int c)
{
...
}
test(arr,3,5)
其实还有另外一种写法,涉及到对数组的不同理解。
二维数组其实可以看作是一维数组的数组,每个元素都是一个一维数组,那么二维数组的首元素就是第一行,是个一维数组。
所以,根据数组名是数组首元素地址这个规则,二维数组的数组名表示的就是第一行的地址,是一维数组的地址。根据上面的例子,第一行的一维数组的类型就是int [5],所以第一行的地址的类型就是数组指针类型int (*) [5]。那就意味着二位数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址,那么形参也是可以写成指针形式的:
#include <stdio.h>
void test(int (*p)[5],int r,int c)
{
int i = 0;
int j = 0;
for(i = 0;i < r;i++)
{
for(i = 0;j < c;j++)
{
printf("%d ",*(*(p+i)+j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
test(arr,3,5);
return 0 ;
}
总结:二维数组传参,形参的部分可以写成数组,也可以写成指针形式。
4.函数指针变量
4.1 函数指针变量的创建
函数指针变量是一种指针变量,是用来存放函数地址的,能通过地址调用函数的。
函数是有地址的,函数名就是函数的地址,可以通过&函数名的方式获得函数的地址。如果要将函数的地址存放起来,就得创建函数指针变量,函数指针变量的写法和数组指针非常类似。
void test()
{
printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;
int Add(int x,int y)
{
return x+y;
}
int(*pf3)(int,int) = Add;
int(*pf3)(int x,int y) = &Add;//x和y写上或者省略都是可以的
函数指针解析:int(*pf3)(int x,int y)
int为pf3指向函数的返回类型,(*pf3)为函数指针变量名,(int x,int y)为pf3指向函数的参数类型和个数的交代。
int (*)(int x,int y)为pf3函数指针变量的类型。
4.2 函数指针变量的使用
通过函数指针调用指针指向的函数。
4.3 两段有趣的代码
代码1:
(*(void(*)())0);
代码2:
void (*signal(int ,void(*)(int)))(int);
4.3.1 typedef关键字
typedef是用来类型重命名,可以将复杂类型,简单化。
比如,unsigned int写起来不方便,如果写成uint方便多了。那么我们可以使用:
typedef unsigned int uint;
//将unsigned int重命名为uint
如果是指针类型,也可以重命名,比如将int*重命名为ptr_t这样写:
typedef int* ptr_t;
但是如果是数组指针和函数指针就会稍微有点区别:
比如数组指针类型int(*)[5]需要重命名为parr_t:
typedef int(*parr_t)[5];//新的类型名必须在*的右边
函数指针类型的重命名也是一样的,比如将void(*)(int)类型重命名为pf_t,就可以这样写:
typedef void(*pfun_t)(int);//新的类型名必须在*的右边
5.函数指针数组
数组是一个存放相同类型数据的存储空间,我们已经学习了指针数组,比如
int * arr[10];
//数组的每个元素是int*
类似的,我们也可以把函数的地址存到一个数组中,那这个数组叫做函数指针数组,
int (*parr1[3])();
parr1先和[ ]结合,说明parr1是数组,数组的内容是int(*)()类型的函数指针。