IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c语言进阶: 指针的进阶(上) -> 正文阅读

[C++知识库]c语言进阶: 指针的进阶(上)

首先 在学习新知识之前 我们先来回顾下之前的学习的内容

1 指针是个变量 用来存放地址 地址唯一标识的一块内存空间
2 指针的大小是固定的4/8字节(32位平台/64位平台)
3 指针有类型的 指针的类型决定了两点 一个是指针操作的权限以及±整数的步长
4 指针的运算

初识指针

熟悉指针

一. 字符指针

1. 解析

在指针的类型中我们知道有一种指针的类型位字符指针

char *

我们一般这么使用

int main()
{
	char ch = 'w';
	// ch里存放的字符是一个w
	char* pc = &ch;
	// 指针pc里面存放ch的地址
	*pc = 'w';
	return 0;
}

还有一种使用方式如下

int main()
{
	const char* ps = "hello world";
	printf("%s\n", ps);
	return 0;
}

在这里插入图片描述

这里我们存放的ps其实不是一个字符 而是一个字符串的首地址

我们打印字符串的时候只需要把首地址传进去就可以打印出来一个完整的字符串

要验证这个说法我们可以使用数组

int main()
{
	char arr[] = "hello world";
	// arr就是数组的首地址
	printf("%s\n", arr);
}

在这里插入图片描述

我再画图给同学们解析一下

在这里插入图片描述

2. 题目巩固

int main()
{
	char str1[] = "hello world";
	char str2[] = "hello world";
	const char* str3 = "hello world";
	const char* str4 = "hello world";
	if (str1==str2)
	{
		printf("same\n");
	}
	else
	{
		printf("not same\n");
	}
	printf("----------------------------\n");
	if (str3==str4)
	{
		printf("same\n");
	}
	else
	{
		printf("not same\n");
	}
	return 0;
}

我们来分析下
str1和str2是两个数组 相同的常量字符串去初始化 不同的数组会开辟不同的内存块 所以str1和str2不同
str3和str4是指向同一个常量字符串 会储存在单独的一个内存区域 所以说str3和str4相同
我们打印出来看看

在这里插入图片描述

二. 指针数组

在指针章节中我们也学习了指针数组 指针数组是存放指针的数组

这里我们再复习一下 下面的指针数组是什么意思?

int *arr1[10]
一个指针数组 里面存放的int类型的指针
char *arr2[4]
一个指针数组 里面存放的是char类型的指针
char **arr3[5]
一个二级指针数组 存放的二级指针

三. 数组指针

1. 数组指针的定义

我们先来看两段代码

int* p[10];//1
int(*p)[10];//2

因为1中的变量名先和p结合 所以说它是一个数组
因为2中的解引用标识符先和p结合 所以说它是一个指针

2. &数组名和数组名的区别

还是一样 先看代码

int arr[10];

对于这段代码来说 arr和&arr分别是啥?
(其实这一段已经在我们前面的数组详解中已经介绍过了 我们再来复习下)

我们都知道arr是数组名,数组名表示数组首元素的地址

那么&arr数组名到底是啥?

我们来看下面的一段代码:
在这里插入图片描述我们可以发现地址是一样的

我们再来看下面的代码

在这里插入图片描述
&arr步进的地址实际上是40

arr步进的地址实际上是4

3. 数组指针的使用

老规矩 看代码

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int(*p)[10] = &arr;
	return 0;
}

使用

在这里插入图片描述

4. 总结

我们来逐个分析下 下面代码的意思

int arr[5]//1
int *p[10]//2
int (*p)[10]//3
int (*p[10])[5]//4

第一 是一个数组 数组里面有五个元素 每个元素都是int类型的

第二 是一个数组 数组里面有十个元素 每个元素都是整型指针类型的

第三 是一个指针 指针指向一个数组 数组里面有十个元素

第四 是一个数组 数组里每个元素类型是数组指针 每个数组指针指向的数组有五个元素
这个比较难理解 我画图给大家分析下

在这里插入图片描述

大概就是上图的结构

四. 数组传参 指针传参

1. 一维数组的传参

看代码

void test(int arr[])
{}
void test(int arr[10])
{}
void test(int *arr)
{}
void test2(int *arr[20])
{}
void test2(int **arr)
{}

int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test2(arr2);
	return 0;
}

我们说test传参的时候 arr[10]都是可以传递进去的 因为传递数组本质上是传递一个地址
这一点在我前面的博客有过介绍 大家感兴趣可以参考这两篇文章

函数栈帧上

函数栈帧下

而对于test2来说 arr2传递是本质也是一个数组 数组里面存放的都是指针
所以说*arr[20]是可以传递的
对于最后一个二级指针来说 我们传递进去的是一个一级指针的数组 本质是把一级指针的地址传递进去 用二级指针来接受 当然没有问题

2. 二维数组的传参

看代码


void test(int *arr)
{}
void test(int *arr[5])
{}
void test(int (*arr)[5])
{}
void test(int **arr)
{}
int main()
{
	int arr[3][5] = { 0 };
	test(arr);
	return 0;
}

分析

第一个指针指向整型 而我们的二维数组的地址实际上表示一行的所有元素 所以显然不行
第二个是一个数组 而我们要传递一个指针进去 显然不可以
第三个是一个数组指针 每一个指针指向五个元素 和我们的数组相符 所以可以传递进去
第四个是一个二级指针 指向的要是一级指针的地址 这里显然不行

3. 一级指针的传参

void print(int* p, int sz) 
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d\n", *(p + i));
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(p, sz);
	return 0;
}

一级指针的传参其实没什么花里胡哨的 直接把指针传进去就可以了
我们主要思考的是 当一个函数的参数部分作为一级指针的时候 函数能接收什么参数呢
我们说 可以传递
1 一维数组的数组名
2 一维数组地址
3 一级指针

4. 二级指针的传参

代码和一级指针差不多 大家可以模仿者写一下 传参其实也没有什么花里胡哨的东西
还是一样的问题
当一个函数的参数部分作为二级指针的时候函数能够接收什么参数呢

1 一级指针的地址
2 二级指针
3 指针数组的数组名 (ex:int *p[5])

因为此时的p是一个数组 数组里面五个元素都是一级指针

当我们传递这个数组名的时候实际上是传递的首元素的地址 也就是说一级指针的地址

当然可以被二级指针接收啦

五. 函数指针

首先 看代码

void test()
{
	printf("hehe\n");
}
int main()
{
	printf("%p\n", test);
	printf("%p\n", &test);
	return 0;
}

在这里插入图片描述
我们可以发现 打印的两个地址是一模一样的

我们我们应该怎么设计一个指针来存储函数的地址呢?

void (*pfun1)();//1
void* pfun2();//2

我们想想看 到底是1还是2可以存储呢?

答案是1

这个问题的关键和数组知识是相同的 看变量先和谁结合 所以说1是个函数指针 可以存储函数的地址

1. 函数指针的创建

int (*p)(int x, int y) = Add;// 前面需要加参数 后面不需要

2. 函数指针的使用

int sum = p(3, 5);//解引用操作符可用可不用 对于结果没有影响

完成运行结果

在这里插入图片描述

六. 函数指针数组

我们都知道 数组是一个存放相同类型数据的存储空间 那我们已经学习了指针数组

那么函数有没有对应的指针数组呢? 如果有那应该怎么定义呢?

1. 函数指针数组的定义

我们说 函数指针数组的定义 应该遵循以下格式

int (*p[10])();

首先p和[]结合说明了它是一个数组
我们再将它拿掉可以发现 数组的内容就是
int (*)() 类型的函数指针。

2. 函数指针数组的用途:转移表(以计算器为例)

我们首先来写一段代码实现计算器的功能

int add(int x, int y)
{
	return x + y;
}

int sub(int x, int y)
{
	return x - y;
}

int mul(int x, int y)
{
	return x * y;
}

int div(int x, int y)
{
	return x / y;
}

int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	do
	{
		printf("***********************\n");
		printf("1:add               2:sub\n");
		printf("3:mul               4:div\n");
		printf("***********************\n");
		printf("请选择 ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个操作数\n");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个操作数\n");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数\n");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数\n");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("%d\n", ret);
			break;
		default:
			printf("输入错误\n");
			break;
		}
	} while (input);
	return 0;
}

那么我们再使用函数指针来实现试试

最重要的代码是这两行

int (*p[5])(int x, int y) = { 0,add,sub,mul,div };
ret = (*p[input])(x, y);

整体代码如下:

int add(int x, int y)
{
	return x + y;
}

int sub(int x, int y)
{
	return x - y;
}

int mul(int x, int y)
{
	return x * y;
}

int div(int x, int y)
{
	return x / y;
}

int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	int (*p[5])(int x, int y) = { 0,add,sub,mul,div };
	do
	{
		printf("***********************\n");
		printf("1:add               2:sub\n");
		printf("3:mul               4:div\n");
		printf("***********************\n");
		printf("请选择 ");
		scanf("%d", &input);
		if (input<=4 && input >=1)
		{
			printf("请输入操作数: \n");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("输入有误\n");
		}
	} while (input);
	return 0;
}

7. 指向函数指针数组的指针

这个稍微了解下就好


void test(const char* str)
{
	printf("%s\n", str);
}

int main()
{
	void (*pfun)(const char*) = test;
	// 一个函数指针
	void (*pfunarr[5])(const char* str);
	//函数指针数组
	pfunarr[0] = test;
	//数组的第一个元素是test
	void (*(*ppfunarr)[5])(const char*) = &pfunarr;
	//指向函数指针数组的pfunarr的指针ppfunarr
	return 0;
}

以上就是本篇博客的全部内容啦 由于博主才疏学浅 所以难免会出现纰漏 希望大佬们看到错误之后能够

不吝赐教 在评论区或者私信指正 博主一定及时修正

那么大家下期再见咯

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-21 00:08:17  更:2022-09-21 00:11:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:45:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码