二级指针
指针是变量,那么也就有相应的地址。指向指针地址的指针,叫做二级指针。
int a = 0;
int* p = &a;
int** p1 = &p;
在以上代码中,p1就是二级指针,它的类型为int**
,它解引用一层,即*p1
,能得到p,即它所指向的内容(虽然也是个指针)
同理可得,三级指针就是int***
,以此类推
字符指针
字符指针类型为char*
,但是它在存储数据的时候略有不同。
如以上代码所示,字符指针类型初始化时可以使用地址也可以使用字符串
- 使用地址初始化时,使用方法和int相同
- 使用字符串初始化时,相当于把首个字母的地址放入了这个指针变量里面
但是需要注意的是,用字符串形式输出时程序是会一直往后读取直到遇到\0
为止,所以可能出现越界访问的情况,就如同以上的乱码
#include<stdio.h>
int main()
{
char str1[]="abcd";
char str2[]="abcd";
char* str3="efgh";
char* str4="efgh";
if(str1 == str2)
printf("1\n");
if(str3 == str4)
printf("2\n");
return 0;
}
以上代码的结果是打印了一个2,也就是说str3和str4在值上是相等的
变量都具有两个属性:值属性和类型属性
首先str3初始化,指向了一块空间,该空间存放“efgh”这个字符串,然后创建str4,也指向“efgh”,此时就没必要再创建新的空间保存它了,直接指向了和str3一样的空间。所以str3和str4在值上是一样的(不过这俩是两个变量,地址还是不一样的)
此时efgh为字符串常量,是不可以修改的
而str1和str2都是开辟了两块不同的空间保存了两份一样的内容,在数组里的内容是可以修改的
数组指针
数组指针就是指向数组的指针,如int(*arr)[5]
,它的类型是int(*)[5]
- 注意:[]的优先级高于*,所以要加括号保证*和arr先结合
二维数组如何传参?
函数中,数组的传参实际上是一种传址调用,调用的是数组首元素的地址
那么二维数组就是将首行(每一行看作一个一维数组)的地址传了过去
void test(int arr[][5])
{
//......
}
int main()
{
int arr[3][4]={0};
test(arr);
return 0;
}
原来我们用这种方式接收传过来的数组
还可以:
void test(int(*arr)[5])//这是接收了首元素arr[0](一个一维数组)的地址
{
//......
}
int main()
{
int arr[3][4]={0};
test(arr);
return 0;
}
以上代码用了指针的形式接收了传过来的数组,其中的int(*arr)[5]
中的arr
实际上接收的是arr[0]
(一个一维数组的数组名,也是它的地址)
在二维数组中,数组名代表的是首行的地址,以上代码的arr+1
表示跳过一行的元素
函数指针
函数指针就是指向一个函数的指针
#include<stdio.h>
void test(int a, int b)
{
//......
}
int main()
{
void(*pf)(int, int)=test;
//返回类型 参数类型
}
在以上代码中,pf就是一个函数指针,指向的是有两个int类型的参数,返回类型为void的函数
此处等号后面用test或者&test都可以,这两种都表示函数地址
有了函数地址就可以调用这个函数了
typedef函数
这个函数可以重命名一个类型
typedef unsigned int unint;
以上代码就是把unsigned int重命名成int
比如下面的代码:
( * (void (*)()) 0 )();
这个代码就是将0强制类型转换成void (*)()
类型(一个函数指针类型)
然后再对0这个地址的函数进行调用(前面的*解引用可以省略)
对于指针类型重命名,新的名字要在*的右边
typedef void(*vd)();
( *(vd)0 )();
//函数地址 参数类型(没有)
这段代码和上面的是一样的
void (*signal(int, void(*)(int)))(int);
这段代码是在声明一个叫signal的函数
signal函数有两个参数,一个是int,一个是void( * )(int)类型的函数指针
signal的返回值是一个void ( * )(int)类型的指针
typedef void(*vd)(int);
vd signal(int, vd);
如果把void( * )(int)简化一下,这段代码就会好看很多
函数指针数组
函数指针数组是一个数组,用于存放一组函数(这些函数的参数类型和返回类型都要相同)
int (*p[4])={test1, test2, test3, test4};
这个数组名是p,去掉p等号前面就是它的类型,即int(*)[4]
转移表
转移表就是将一组相似的函数放在一个数组中,方便调用
例如简易计算器的实现:
int Div(int x, int y)
{
return x / y;
}
int Mul(int x, int y)
{
return x * y;
}
int Sub(int x, int y)
{
return x - y;
}
int Add(int x, int y)
{
return x + y;
}
int main()
{
int i = 0;
int (*p[5])(int x, int y)={0, Add, Sub, Mul, Div};
do{
printf("1.Add ");
printf("2.Sub ");
printf("3.Mul ");
printf("4.Div\n");
printf("请选择模式:");
scanf("%d", &i);
if(i > 0 && i < 4)
{
int x = 0;
int y = 0;
printf("请输入操作数:");
scanf("%d %d", &x, &y);
int ret = p[i](x, y);
printf("%d\n", ret);
}
}while(i);
}