指针的进阶
本章重点
- 字符指针
- 数组指针
- 指针数组
- 数组传参和指针传参
- 函数指针
- 函数指针数组
————————————————————————————————————————————————
正文开始
在之前的初阶章节,我们已经简单学习了指针,我们知道了指针的概念:
- 指针就是一个变量,用来存放地址,地址唯一标识一块内存空间
- 地址的大小是固定的4/8个字节(在x86系统下是4个字节,在x64系统下是8个字节)
- 指针是有类型,指针的类型决定了指针的±步长,指针解引用操作时候的权限
- 指针的运算
1.字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char*
存放字符类型(char)变量的地址!
1.1 示例
1.2 字符指针打印的“常量字符串"
星号 * 解引用时,只读取了首字符a,所以只打印了字符a,所以我们的字符指针存放的不是整个字符串,而是字符串的首个地址。
1.2 字符指针打印的常量字符串
字符指针打印字符串的时候只需要给一个起始地址,然后程序遇到‘\0’将会停止打印!
例题
#include<stdio.h>
int main()
{
char p1[] = "abcdef";
char p2[] = "abcdef";
char* p3 = "abcdef";
char* p4 = "abcdef";
if (p1 == p2)
printf("p1 and p2 are same!\n");
else
printf("p1 and p2 are not same!\n");
if(p3 == p4)
printf("p3 and p4 are same!\n");
else
printf("p3 and p4 are not same!\n");
return 0;
}
问代码运行结果是什么?
答案:p1 and p2 not are same!
p3 and p4 are same!
- 字符串不可以直接比较为什么?
因为用大于小于等于号,比较的是字符串首地址大小,而不是字符串大小。如,”abcdef“和"abc",比较的是”abcdef“中的a的地址和“abc”中a的地址。
- 为什么不能用数组名直接比较?
因为数组名代表首元素地址(一般情况下),用数组名比较和用字符串直接比较性质是一样的,所以用数组名比较也是不对的。当我们把字符串“abcd”赋值给指针p,p中存的是a的地址
char* p = “abcd”;
- p3,p4为什么可以相等呢?
指针就是一个变量,用来存放地址,地址是一个变量,可进行比较大小,而字符变量则不可以。
2.指针数组
指针数组:存放指针的数组,本质是数组,里边存放的是指针!
// char * arr1[5] = {“lisi”,“wangwu”,“zhangsan”,“wangcai”,“laobiao”};
// char a, b, c;
// char * arr2[3] = {a,b,c};
2.1 指针数组实现打印字符串
#include<stdio.h>
**//指针数组实现同时打印多个字符串**
int main()
{
char* arr[5] = { "lisi","wangwu","zhangsan","wangcai","laobiao" };
int i;
for (i = 0; i < 5; i++)
{
printf("%s\n", arr[i]);
}
return 0;
}
指针数组是存放指针的数组,数组arr里,存放的是每一个字符串的首地址,然后使用%s,进行打印字符串的操作。
2.2 指针数组实现二维数组
#include<stdio.h>
//
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* arr[] = { arr1,arr2,arr3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);//arr[0] == arr1,arr[1] == arr2,
} //arr[2] == arr3
printf("\n");
}
return 0;
}
2.3指针数组的类别
int * arr[10] //整形数组指针
char * arr[5] //字符数组指针
char ** arr[4] //二级指针数组
3.数组指针
数组指针:指向数组的指针,本质上是指针,存放的是数组的各个元素的地址!
//int arr[10];
//int ( *p)[10] = &arr;
//char (*p)[5];
//int (*p)[n];
3.1 通过数组指针打印数组元素
#include<stdio.h>
//通过数组指针打印数组元素
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int(*p)[10] = &arr;//p = &arr,(*p) = arr, (*p)[i] = arr[i]
for (int i = 0; i < 10; i++)
{
printf("%d ", (*p)[i]);
}
return 0;
}
//p = &arr,(*p) = arr, (*p)[i] = arr[i]
3.2 arr与&arr的区别
arr是自定义数组的名称,无其它特殊含义
在C语言中%p是格式控制符和%d类型相同。
根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。
实际上: &arr 表示的是数组的地址,而不是数组首元素的地址。(细细体会一下)
本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40
3.3 二维数组的传参
#include<stdio.h>
//二维数组传参的实现
void print(int(*p)[5], int line)
{
int i, j;
for (i = 0; i < line; i++)
{
for (j = 0; j < 5; 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} };
int line = sizeof(arr) / sizeof(arr[0]);//计算二维数组的行数
print(arr, line);
return 0;
}
3.4 数组指针的类别
int ( * )[n]—整型数组指针
char ( * )[n]—字符数组指针
float ( * )[n]—浮点型数组指针
4. 数组传参和指针传参
4.1 一维数组传参
4.2 二维数组传参
二维数组传参时,注意事项:
- 行数可以省略,但列数不可以省略,因为我们可以不知道多少行,但一行多少个元素我们必须知道,这样操作方便!
- 要掌握通过数组指针实现二维数组传参的方法!
- 二级指针接收的是一级指针的地址,即int * p = a;int ** pp =&p;
- 而指针数组是一个数组,不能够接收地址!!!
4.3 一级指针传参
4.4 二级指针传参
5.函数指针
前边我们学习了数组指针,理解了数组指针的本质,而函数指针与数组指针的命名方式相同基本相同,先是指针后是函数!
5.1 函数名是地址
Add和&Add都是函数的首地址,没有区别!!!
5.2 函数指针的应用
函数指针,顾名思义是存放函数地址的指针,能够访问函数进而使用函数!!!
我们使用函数指针实现调用函数(功能:求得两个整数的和)
因为Add和&Add都是函数的首地址且p = Add,所以p(a, b) 和Add(a, b)没有区别!!!
5.3 函数指针的类别
较易:
int(*p)() 整形函数指针
char(*p)() 字符函数指针
float(*p)() 浮点型函数指针
较难(提升):
代码1
( *( void (*)() )0 )();
void (*)():是函数指针类型,没有参数,返回值是void
(void (*)())0:把0强制类型转化为void (*)(),把0当作一个函数的地址
( *( void (*)() )0 )():然后*解引用,取出函数地址,调用没有参数的函数使用。
代码2
void ( *signal( int , void(*)(int) ) )(int);
上述代码是一个函数声明
声明的函数叫做:signal
signal第一个参数是int类型的
signal第二个参数是一个函数指针类型,该函数指针指向的参数为int类型的,返回类型是void
signal函数返回类型也是一个函数指针类型,该函数指向的函数参数的类型是int类型,返回值是void
6.函数指针数组
函数指针数组:指向函数的指针数组,本质上是指针数组,存放的是函数的首地址!
6.1 函数指针数组的解剖
函数指针:
函数指针类型 (*p)(参数类型)
int (*p)(int, int)
指针数组:
指针数组类型 *名称[]
int * arr[10]
函数指针数组,则先是指针数组后是函数
则:函数指针数组类型 (指针数组)(参数类型)
int (*arr[10])(int, int)
6.2 函数指针数组实现函数存储
- 函数指针数组:指向函数的指针数组,本质上是指针数组,存放的是函数的首地址!
使用函数指针数组来实现简单计算机的实现!
float Mul(float x, float y)
{
return x * y;
}
float Div(float x, float y)
{
return x / y;
}
float Add(float x, float y)
{
return x + y;
}
float Sub(float x, float y)
{
return x - y;
}
void menu()
{
printf("**********************\n");
printf("*******1.Mul**********\n");
printf("*******2.Div**********\n");
printf("*******3.Add**********\n");
printf("*******4.Sub**********\n");
printf("*******0.No **********\n");
printf("**********************\n");
}
int main()
{
float (*arr[4])(float, float) = { Mul ,Div ,Add,Sub };
float x = 0, y = 0;
int i = 1;
while(i)
{
menu();
arr:
scanf("%d", &i);
if (i >= 1 && i <= 4)
{
printf("请输入两个数>:");
scanf("%f %f", &x, &y);
printf("=%.1f\n", arr[i - 1](x, y));
}
else if (i != 0)
{
printf("输入错误,请重新输入>:\n");
goto arr;
}
}
return 0;
}
函数指针数组实现计算机和switch分支语句实现简单的计算机,使用函数指针数组不论是效率还是内存都更要好!
大家可以下去自己试一下,加深理解!!!🐱👤🐱👤🐱👤