指针的进阶

指针的进阶

本章重点

  1. 字符指针
  2. 数组指针
  3. 指针数组
  4. 数组传参和指针传参
  5. 函数指针
  6. 函数指针数组

————————————————————————————————————————————————
正文开始

在之前的初阶章节,我们已经简单学习了指针,我们知道了指针的概念:

  • 指针就是一个变量,用来存放地址,地址唯一标识一块内存空间
  • 地址的大小是固定的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 (*)():是函数指针类型,没有参数,返回值是voidvoid (*)())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分支语句实现简单的计算机,使用函数指针数组不论是效率还是内存都更要好!
大家可以下去自己试一下,加深理解!!!🐱‍👤🐱‍👤🐱‍👤

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值