指针详解第三部分

news2024/12/24 10:21:10

1. 字符指针变量

一种指针类型为字符指针 char*(下面有两个代码表示了两种不同的用法)

 

int main()
{
	char ch = 'w';
	char* pc = &ch;
	*pc = 'w';
	return 0;
}

int main()
{
	const char* pstr = "hello bit.";//这⾥是把⼀个字符串放到pstr指针变量⾥了吗?
	printf("%s\n", pstr);
	return 0;
}

 第一个代码 存了一个字符w。将字符 w 存到了 变量ch的地址上。

第二个代码 存了一个字符串。和第一个 指针pc 不同,他不是把 hello bit 存到了pstr 的地址里。而是由pstr 指针指向第一个字符h 的地址。然后找到这串字符。

1.1一道剑指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");
 else
 printf("str1 and str2 are not same\n");
 
 if(str3 ==str4)
 printf("str3 and str4 are same\n");
 else
 printf("str3 and str4 are not same\n");
 
 return 0;
}

结果如图: 

 

 const 的意思是让指针 str3 和 str4 所指向的地址不会改变。

这题我是这样理解的

str1和str2 的区别在于这两个都是字符数组,设置他们相当于在内存开辟了一块的新的区域,所以两个字符数组的地址是不同的,首元素的地址也不可能相同。

str3 和str4 相同的原因是 两个指针指向的是一个字符串,他们并不是一个数组,并不能储存这一串字符,所以存储的这一串常量字符串的首元素的地址。通过首元素找到后续字符。

C/C++会把常量字符串存储到单独的⼀个内存区域,
当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始 化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

2. 数组指针变量

2.1数组指针变量是什么

数组指针变量就是数组的指针。和之前的指针数组不同的是 一个是指针一个是数组。

今天学习的数组指针是一个指针用来指向一个数组。

整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针。
浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。

 

int *p1[10];
int (*p2)[10];

这两的区别是啥呢? 

首先第一个因为 【】的优先级大于 * 所以p1先和 【】结合,所以第一个是一个指针数组。里面存放了10个指针。p1【10】是一个数组 里面存储的是 int*的变量。所以指针数组

第二个加了一个 ()让 *和p2先结合,确定他是一个指针,后在和 【10】结合说明他是一个指针指向了一个数组。这个数组中 有 10个元素。*p2 说明他是一个指针,int 数组名【10】说明他是一个数组。(()的优先级最高 )

 2.2数组指针怎么初始化

int arr[10] = {0};
&arr;



int(*p)[10] = &arr;

int arr【10】 = {0}是创建一个数组里面元素为10个int。int(*p)【10】= &arr,(&arr是特殊情况,&arr取出的是整个数组的地址。)用一个可以接受10个 int 的 指针p 去指向这个数组的地址 。

提示:如果要存一个数组的指针首先你的类型得和数组一样。打开VS的监视可以看到:如下图。

 

int (*p) [ 10 ] = &arr;
|      |       |
|      |       |
|      |      p 指向数组的元素个数
|      p 是数组指针变量名
p 指向的数组的元素类型

3. ⼆维数组传参的本质

#include <stdio.h>
void test(int a[3][5], int r, int c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			printf("%d ", a[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;
}

 这里传递了arr数组名,是元素的首地址,用int a【3】【5】接收是为了保证类型相同。

然后打印出二维数组即可。

当然除了这样的写法另一种写法,但在介绍前我们要搞懂二维数组的传参本质。 

二维数组其实就是存储了一堆一维数组的数组,所以二维数组里存的就是一维数组。

那是不是二维数组的单位就是 一个一维数组。那他的首元素就是一个一维数组。所以二维数组的数组名就是 一行一维数组的地址。那就可以用一个一维数组的指针去接收。

第一行的数组类型是int [5],那就可以用这样的指针去接收int(*)[5]。这样写也是对的。而且这样传进去的形参是可以改变实参,因为是实参的地址。

 总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。

4. 函数指针变量

4.1 函数指针变量的创建

什么是函数指针变量?

同理他也是一个指针,指向的是一个函数。

那么函数也有地址码?(看看下面这串代码)

#include <stdio.h>
int main()
{
 printf("test: %p\n", test);
 printf("&test: %p\n", &test);
 return 0;
}

 

可以看出函数也是有地址,且函数名和&函数名取出的地址是相同的。 (和数组不同,这两个地址是相同的。都是函数的地址)。

那我们想把函数的地址存起来,是不是就可以用一个指针来接收了?且写法和数组指针相似

看下面这串代码:

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;

这串代码的理解是:首先函数的指针你的返回类型得和你的函数相同。

第二 和数组的指针相同也要用到()来保证这是一个指针。

第三 后面的()里面要填入和函数形参里一样的类型,可以不用写出具体变量名,但类型要相同。

最后解析如下:

 

int    (*pf3)      ( int x, int y)
|         |              ------------
|         |                   |
|         |                   pf3 指向函数的参数类型和个数的交代
|         函数指针变量名
pf3 指向函数的返回类型
int (*) ( int x, int y)   //pf3 函数指针变量的类型

4.2函数指针变量的使用 

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int(*pf3)(int, int) = Add;

	printf("%d\n", (*pf3)(2, 3));
	printf("%d\n", pf3(3, 5));
	return 0;
}

这两个写法都能得出结果。 

 4.3.1 typedef关键字

首先typedef 是用来将类型的名字重新命名的。

比如 一个类型是 unsigned int 太多了 你觉得不方便你就可以这样写:

typedef unsigned int uint;
//将unsigned int 重命名为uint

 当然指针类型也可以重命名。和上面同理。int* 等指针类型 也是可以重名的。

但数组指针和 函数指针的重命名不太一样:

typedef int(*parr_t)[5];//新的类型名必须在*的右边
typedef void(*pfun_t)(int);//新的类型名必须在*的右边

 这两个类型的重命名必须把重新定义的类型名写在小括号里面。

5. 函数指针数组

函数指针的数组,就是一个数组里面放的全是函数指针。那这个数组的类型怎么写呢?看下面这三个代码判断一下:

int (*parr1[3])();
int *parr2[3]();
int (*)() parr3[3];

答案是parr1.解析是:在括号里面parr1先与【3】结合说明这是一个数组。 int (*parr1[3])()

int (*)()类型是函数的指针。parr1是数组名。里面存了3个int (*)()

 

6. 转移表

转移表就是用函数指针数组来取出内容。

下面两串 代码 ,第一个代码是一个普通计算器的写法。

第二个则是用函数指针数组来写的计算器写法。

#include <stdio.h>
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}
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(" 0:exit \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("ret = %d\n", ret);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}
#include <stdio.h>
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}
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(" 0:exit \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
			printf("ret = %d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
		}
		else
		{
			printf("输⼊有误\n");
		}
	} while (input);
	return 0;
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1059613.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是JWT?深入理解JWT从原理到应用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《ELement》。&#x1f3af;&#x1f3af; &#x1…

使用 Python 和 wxPython 进行批量文件扩展名替换

引言&#xff1a; 在日常的文件管理中&#xff0c;有时我们需要将一大批文件的扩展名进行替换。手动一个个重命名文件是一项繁琐的任务&#xff0c;但是使用 Python 编程语言和 wxPython 模块可以轻松地实现这一功能。本文将介绍如何使用 Python 和 wxPython 创建一个简单的图形…

Python学习笔记之分支结构与循环结构

Python学习笔记之分支结构与循环结构 一、分支结构 使用关键字if、elif、else 练习1&#xff1a;使用分支结构实现分段函数求值 """分段函数求值""" x float(input("x "))if x > 1:y 3 * x - 5 elif x < -1:y 5 * x 3…

开启创意思维,畅享Mindomo Desktop for Mac思维导图之旅

在数字化时代&#xff0c;我们需要一个强大而直观的工具来整理和展现我们的思维。Mindomo Desktop for Mac&#xff08;Mindomo&#xff09;作为一款免费的思维导图软件&#xff0c;将为您提供卓越的创意思维体验。 Mindomo拥有直观的界面和丰富的功能&#xff0c;让您能够方便…

手搭手Mybatis-Plus多数据源异构数据迁移案例

环境介绍 技术栈 springbootmybatis-plusdruidbaomidoumysqloracledm 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis 2.3.1 pom.xml所需依赖 <dependencies><dependency><groupId>org.springframework.…

【Golang】包

包 Go语言是使用包来组织源代码的&#xff0c;包&#xff08;package&#xff09;是多个 Go 源码的集合&#xff0c;是一种高级的代码复用方案 Go语言中为我们提供了很多内置包&#xff0c;如 fmt、os、io 等。 任何源代码文件必须属于某个包&#xff0c;同时源码文件的第一…

【2】c++设计模式——>UML表示类之间的继承关系

继承也叫作泛化&#xff08;Generalization&#xff09;&#xff0c;用于描述父子类之间的关系&#xff0c;父类又称为基类或者超类&#xff0c;子类又称作派生类。在UML中&#xff0c;继承关系用带空心三角形的实线来表示。 关于继承关系一共有两种&#xff1a;普通继承关系和…

【共享模型-----管程】

文章目录 1. 为什么会出现线程安全问题2. synchronized 解决方案2.1 线程八锁 3. 变量的线程安全分析3.1 局部变量线程安全分析3.2 常见线程安全类 1. 为什么会出现线程安全问题 一段代码块内如果存在对共享资源的多线程读写操作&#xff0c;称这段代码块为临界区 共享资源&a…

一张图看懂 8 种网络协议

什么是网络协议&#xff1f; 网络协议就是计算机之间沟通的语言&#xff0c;为了有效地交流&#xff0c;计算机之间需要一种共同的规则或协议&#xff0c;就像我们和老外沟通之前&#xff0c;要先商量好用哪种语言&#xff0c;要么大家都说中文&#xff0c;要么大家都说英语&a…

优思学院|六西格玛将烹饪和美味提升至极致

最近&#xff0c;我们曾提到一个美国男子如何利用六西格玛来控制糖尿病。这表明六西格玛逐渐被认为是一个不仅可以在工作场所之外使用&#xff0c;尤其不仅限于制造业的系统。 六西格玛的核心理念是改进过程的质量&#xff0c;从而改善最终结果。如果你做了晚餐或尝试了一道新…

ESP32设备驱动-OLED显示BME280传感器数据

OLED显示BME280传感器数据 文章目录 OLED显示BME280传感器数据1、BME280介绍2、硬件准备3、软件准备4、代码实现在本文中,我们将介绍如何使用OLED显示BME280传感器的数据。 1、BME280介绍 BME280 传感器用于测量有关环境温度、大气压力和相对湿度的读数。 它主要用于以低功耗…

2023/10/4 -- ARM

今日任务&#xff1a;QT实现TCP服务器客户端搭建的代码&#xff0c;现象 ser&#xff1a; #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);server new QTcpSe…

JMeter性能分析实战一:日常登录接口

负载测试 日常需求&#xff1a;负载测试&#xff01; 对于桥的负载测试&#xff1a;我给你20t的一排车辆&#xff0c;看你能不能撑得住20t&#xff01; 对于系统的负载测试&#xff1a; 逐步增加负载&#xff0c;便于问题的发现和定位&#xff0c;不要操之过急。逐步增加负载…

由Long类型引发的生产事故

事情原由 今天测试忽然在群里发了一个看似非常简单的线上问题&#xff0c;具体是&#xff1a;在后台通过订单编号(orderId)修改订单信息时&#xff0c;修改不成功 &#xff0c;修改前后的订单数据完全没有发生变化。第一眼看到这个问题的时候&#xff0c;我心想后台实现逻辑并不…

CSS 语法

CSS 实例 CSS 规则由两个主要的部分构成&#xff1a;选择器&#xff0c;以及一条或多条声明: 选择器通常是您需要改变样式的 HTML 元素。 每条声明由一个属性和一个值组成。 属性&#xff08;property&#xff09;是您希望设置的样式属性&#xff08;style attribute&#x…

监狱劳动工具管理系统|智工具DW-S308的功能

监狱劳动工具管理系统(智工具DW-S308)是依托互3D技术、云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对工具进行统一管理、分析的信息化、智能化、规范化的系统。 目前监狱的劳动工具管理很多还停留在固定工位&#xff0c;人盯人、人管人等落后的管理模式&#xff…

图谱滤波(Graph Spectral Processing)-1

时域和空域信号的滤波是图像处理的基本技术之一&#xff0c;迄今已得到广泛的研究。Graph Spectral Processing(GSP)可以处理不规则结构的信号&#xff0c;这些信号在数学上用图形表示。利用谱图理论研究了图信号滤波的理论和方法。在图像处理中&#xff0c;图是表示像素形成的…

华为云云耀云服务器L实例评测|RabbitMQ的Docker版本安装 + 延迟插件安装 QQ邮箱和阿里云短信验证码的主题模式发送

前言 最近华为云云耀云服务器L实例上新&#xff0c;也搞了一台来玩&#xff0c;期间遇到各种问题&#xff0c;在解决问题的过程中学到不少和运维相关的知识。 本篇博客介绍RabbitMQ的Docker版本安装和配置&#xff0c;延迟插件的安装&#xff1b;结合QQ邮箱和阿里云短信验证码…

Gurobi设置初始可行解

目录 1. 决策变量的Start属性直接设置变量的初始值 1.1 Start&#xff1a;MIP变量的起始值&#xff08;初值&#xff09;double类型&#xff0c;可更改 1.2 StartNodeLimit&#xff1a;限制了在完善一组输入部分变量的初始解时&#xff0c;MIP所探索的分支定界的节点的数量 …

SAAS软件营销——利用人工智能帮助企业了解客户

软件营销部队(Salesforce)是全球领先的客户关系管理(CRM)解决方案供应商之一&#xff0c;其产品和服务旨在帮助企业成长&#xff0c;并追踪与客户的关系。 在1999 年成立时&#xff0c;它就率先提出了通过互联网(现在通常称为“云”)实现“软件即服务”(SaaS)的概念。 SaaS提…