【C语言精髓之指针】结构体指针(->与.两个运算符的区别)
/*** @file * @author jUicE_g2R(qq:3406291309)————彬(bin-必应)* 通信与信息专业大二在读 * @copyright 2023.10* @COPYRIGHT 原创技术笔记:转载需获得博主本人同意,且需标明转载源* @language C/C++* @IDE Base on Microsoft Visual Studio 2022* @state Corrected*/
jUicE_g2R的个人主页
前期回顾,指针*、取地址&、解引用*、引用&
最近帮别人改C语言程序,才发现了自己对这部分还是理解不够深(平时写C++会忽略一些底层的东西,比如指针、地址一类的东西),记录一下。
1 错误示范
//错误示范
#include <stdio.h>
#include <malloc.h>
typedef struct {int id;char name[10];
} nodeN,*pNode;
int main(void) {pNode ptr = (pNode)malloc(sizeof(nodeN)); //node是一个结构体指针变量,malloc为该指针指向的对象开辟了空间scanf_s("%s", ptr->name); //这样写绝对会报:...处 这个文件 引发的异常: ... 写入位置 ... 时发生访问冲突。return 0;
}
2 先来了解一下:点运算符 、 箭头运算符 与 取地址运算符
-
首先, p t r ptr ptr 是一个 指针变量 指针变量 指针变量。
typedef struct* pNode
等同于typedef int* pInt
,是 对 指针类型名 指针类型名 指针类型名 的 重定义 重定义 重定义,而不是定义了一个 名为 p N o d e pNode pNode 的 指针变量 指针变量 指针变量pNode ptr = (pNode)malloc(sizeof(nodeN));
的替换写法为:nodeN* ptr = (pNode)malloc(sizeof(nodeN));
或nodeN* ptr = (*nodeN)malloc(sizeof(nodeN));
-
其次,要知道
->
(箭头运算符) 和.
(点运算符) 近似,都是获取 类(或结构体) 类(或结构体) 类(或结构体) 成员 的 运算符 运算符 运算符(+
、*
等等一类的),这种通过 运算符 运算符 运算符 获取成员的方式叫做: 访问 访问 访问 ,而 访问 访问 访问 的实质就是 获取到数据。 -
再者,需要知道这些 运算符 运算符 运算符 的 优先级 优先级 优先级: 括号运算符 括号运算符 括号运算符 > 点运算符 点运算符 点运算符 > 箭头运算符 箭头运算符 箭头运算符 > 取地址运算符 取地址运算符 取地址运算符
&node.name
、&ptr->name
2-1 然后,需要知道 点运算符 点运算符 点运算符 与 箭头运算符 箭头运算符 箭头运算符 的区别:
区别 | 点运算符 | 箭头运算符 |
---|---|---|
含义 | 成员选择(对象) | 成员选择(指针) |
使用形式 | 对象.成员名 | 对象指针->成员名 |
返回值 | 直接返回访问得到的值 | 返回指向结构体成员的指针 |
调用 | 要求编译器知道结构体的定义 | 不需要知道结构体的定义(因为是指针,无需知道具体定义,只需要知道指着哪个就行【即知道被指对象的地址就可以了】) |
适用性 | 需要在使用 node.name 之前包含该结构体的头文件,这样不太适用于多文件开发【更偏向用于单文件】 | 无需【因为不需要知道定义】 |
简单说:
点运算符 点运算符 点运算符 是 结构体变量 结构体变量 结构体变量 访问 其 成员 的 操作符
箭头运算符 箭头运算符 箭头运算符 是 结构体指针 结构体指针 结构体指针 访问 其 指向的成员变量 的 操作符
3 有如下两种修改方法
2-1 不使用 scanf_s
#define _CRT_SECURE_NO_WARNINGS //必须加在所有.h文件的前面,使编译器不对 “没有使用安全函数scanf_s” 这种行为 进行报错
#include <stdio.h>
#include <malloc.h>
typedef struct {int id;char name[10];
} nodeN,*pNode;
int main(void) {pNode ptr = (pNode)malloc(sizeof(nodeN));scanf("%s", ptr->name); return 0;
}
_CRT_SECURE_NO_WARNINGS
我们鼠标箭头移至 _CRT_SECURE_NO_WARNINGS
这段,右键选择 速览定义 速览定义 速览定义,就可以看到(vcruntime.h
文件的)这一段:
// See note on use of "deprecate" at the top of this file
#define _CRT_DEPRECATE_TEXT(_Text) __declspec(deprecated(_Text))#if defined _CRT_SECURE_NO_DEPRECATE && !defined _CRT_SECURE_NO_WARNINGS#define _CRT_SECURE_NO_WARNINGS
#endif#ifndef _CRT_INSECURE_DEPRECATE#ifdef _CRT_SECURE_NO_WARNINGS#define _CRT_INSECURE_DEPRECATE(_Replacement)#else#define _CRT_INSECURE_DEPRECATE(_Replacement) _CRT_DEPRECATE_TEXT( \"This function or variable may be unsafe. Consider using " \#_Replacement \" instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. " \"See online help for details.")#endif
#endif
其实就相当于:
#ifdef _CRT_SECURE_NO_WARNINGS
//TODO:编译器不对安全函数进行检查
#else
//TODO:编译器必须对安全函数的使用进行检查
#elseif
2-2 使用 scanf_s
注意
安全函数的第三个参数不可少,传入的是变量的字节大小,可以用 s i z e o f ( ) 函数 sizeof()函数 sizeof()函数 获取,也可以手动计算
#include <stdio.h>
#include <malloc.h>
#define NAMESIZE 10
typedef struct {int id;char name[NAMESIZE];
} nodeN,*pNode;
int main(void) {pNode ptr = (pNode)malloc(sizeof(nodeN));scanf_s("%s", ptr->name, NAMESIZE); //这个 sizeof()函数 返回的值必须加上//scanf_s("%s", &(ptr->name), NAMESIZE); //这样也是可以的,&(ptr->name)是一种无效操作return 0;
}
4 进一步理解结构体指针
首先,不得不说: 地址即指针,指针即地址!!! 地址即指针,指针即地址!!! 地址即指针,指针即地址!!!
4-1 取地址运算符&
-
首先,取地址运算符
&
返回的结果一般认为是 地址 地址 地址,实际上返回的就是一个指针(因为指针变量的值就是地址值) -
其次,
scanf_s()
中 是否必要有 取值运算符&
呢?并非如此, 因为scanf_s()
第二个参数就是 传的 地址值。
一种是不知道地址,就要通过&
去获取地址:即 变量 变量 变量 去向左结合&
来获得 变量的地址 变量的地址 变量的地址。
另一种 ,如果知道 变量的地址 变量的地址 变量的地址,可以直接填入(如直接填入 n a m e name name 这个地址)。(这一点下面的 仔细揣摩以下两个概念 仔细揣摩以下两个概念 仔细揣摩以下两个概念 会用的)
4-2 数组名name
//C
#define NAMESIZE 10
char name[NAMESIZE];
-
1、 n a m e name name 这个数组名 是一个指针常量!!!
数组名 即为 数组首地址, n a m e name name 等价于
&name[0]
,数组名 是 数组 第一个元素 的 指针,即 n a m e name name 指向 n a m e 数组 name数组 name数组 的 首元素
下面的例子足以说明 n a m e name name 这个数组名 就是一个地址:
scanf_s("%s", name, NAMESIZE);//终端读入 NAMESIZE 字节大小的 char型 的 数据流,存入到 name数组 里
- 2、 n a m e + i name+i name+i 等价于
&a[i]
n a m e + i name+i name+i 指向 数组 第 i 个 元素
//C:终端读入,向 name数组 的第二个单元 写入 char型 数据
scanf_s("%c", &name[1], NAMESIZE);
scanf_s("%c", name+1, NAMESIZE);
-
3、数组名取地址
&name
是一种 无效 操作n a m e name name 等于
&name
,他们的值 都表示 数组首元素的地址
printf_s("%p\n", &name[0]); //首元素的地址值 的 指标:00D3F9B8
printf_s("%p\n", name); //输出数组首元素的地址:00D3F9B8
printf_s("%p\n", &name); //输出数组本身(即首元素地址)的地址:00D3F9B8
虽然两者值相同,但是他们的 类型 是不相同的:
区别 | name | &name |
---|---|---|
定义 | 指针常量 | 一个指向指针常量的指针(指针的指针) |
类型 | 一级指针 一级指针 一级指针 | 函数指针类型( 二级指针 二级指针 二级指针) |
在这里的类型 | char* | char(*),一个指向返回值为 char型 的函数的指针 |
简单说:&name
指向 n a m e name name, n a m e name name 指向 n a m e 数组 name数组 name数组 的 首元素。
4-3 进一步,上升到 结构体指针 ptr
//C
#define NAMESIZE 10
typedef struct {int id;char name[NMAESIZE];
} nodeN,*pNode;
int main(void){pNode ptr = (pNode)malloc(sizeof(nodeN));//结构体指针nodeN node;//结构体变量
-
1、可以把 一个结构体变量 想象成一个 集成了一种或多种类型 的 一维数组
类推先前的结论: n a m e name name 这个数组名 是一个指针常量,则: p t r 结构体指针 ptr结构体指针 ptr结构体指针 = n o d e 结构体名 node结构体名 node结构体名
p t r 指针 ptr指针 ptr指针 的值为 其 指向对象 指向对象 指向对象(结构体)的 首地址值,或叫做: p t r 指针 ptr指针 ptr指针 指向 结构体的首地址
-
2、修改 和 只访问 结构体变量 结构体变量 结构体变量 中 非数组 成员
通过 scanf_s()终端从外部读入输入 值 算一种 修改(赋值) 参数的方式
通过 printf_s()终端写出到外部 值 算一种 只访问 参数的方式
//node.id实际上就是个变量 而不是 指针
scanf_s("%d", &node.id, sizeof(node.id));
printf_s("%d", node.id, sizeof(node.id));
- 3、修改 和 只访问 结构体指针 结构体指针 结构体指针 中 非数组 成员
//ptr->id,箭头运算符 直接返回 ptr->id指针所指的 成员id的值,而不会返回指针本身!!!
scanf_s("%d", &ptr->id, sizeof(ptr->id));
printf_s("%d", ptr->id, sizeof(ptr->id));
printf_s("%d", (*ptr).id, sizeof(ptr->id));
( ∗ p t r ) . i d (*ptr).id (∗ptr).id 等于 p t r − > i d ptr->id ptr−>id !!!【 ( ∗ p t r ) . i d (*ptr).id (∗ptr).id 中 ( ∗ p t r ) (*ptr) (∗ptr) ,由于有括号, p t r ptr ptr 先与 ∗ * ∗ 相与,这步叫 解引用 解引用 解引用,此时 ( ∗ p t r ) (*ptr) (∗ptr) 与 n o d e node node 等价】
4、修改 和 只访问 结构体指针 结构体指针 结构体指针 中 数组 成员
仔细揣摩以下两个概念
一个就是 在 2-1 提到了: 箭头运算符 箭头运算符 箭头运算符 返回的是 指向结构体成员的指针
另一个就是 对象指针 − > 成员 对象指针->成员 对象指针−>成员 是 值
如果 成员 是 非数组 非数组 非数组,这个 值 就是 对应成员的数据类型的 值
如果 成员 是 数组 数组 数组,这个 值 是个 指针!!!(这个值 为 数组的首地址),这样就好解释为什么是这种改法了:
scanf_s("%s", ptr->name, NAMESIZE);//ptr->name 就是 结构体中name数组成员 的首地址
//scanf_s("%s", ptr->name, sizeof((*ptr).name));
5、修改 和 只访问 结构体变量 结构体变量 结构体变量 中 数组 成员
同理, n o d e . n a m e node.name node.name 也可以获得 n a m e 指针 name指针 name指针 的值
scanf_s("%s", node.name, NAMESIZE);
&(ptr->name) 又是什么玩意?
&(ptr->name)是无效的操作,可以不看这部分内容,因为是个人观点(不代表正确)
ptr->name
是一个 地址值,可以看成一个指针。
&(ptr->name)
这就要涉及 指针的指针 了。(前面说了:取地址运算符 &
返回的结果一般认为是 地址 地址 地址,实际上返回的就是一个指针)
因为单纯的访问指针1 的话,就只需要使用一级指针1, 而操作(修改)指针的话就要使用到 二级指针2,即&(ptr->name)
,就是指针的指针(->(ptr->name))。
由于我们要通过 终端输入的方式 向 n a m e name name 这个结构体成员变量赋值,实质上是在修改指针1,就需要传入3 二级指针2来修改 一级指针1,进而,我们就可以通过这个指针2 来访问和修改 一级指针1 所指向的内存位置上的数据。
1 一级指针:ptr->name
这个指针
2 二级指针:1 的指针【或叫地址】
3 先以 一级指针
为例:
void update(*a){*a=2;}
int main(void) {int a=1;update(&a); //更新整形变量 a 的值,需要传入 a 的指针(用 & 获取)printf_s("%d", a);
}
进一步 二级指针
:
void update(int** pp) {**pp = 2;}int main(void) {int a = 1;int* p = &a; //p指向aupdate(&p); //传入a的二级指针printf_s("%d", a);
}
5 警告 C6011 取消对 NULL 指针 “ptr” 的引用 的解决办法
这个是在给 指针指向的对象 开辟了空间时,即:
//C
pNode ptr = (pNode)malloc(sizeof(nodeN));
//C++
pNode p = new node;
的时候,没有对 " 申请空间的时候可能会存在失败的情况 " 的极端情况进行处理。
解决办法:
//C/C++
while(!ptr){ //直至申请成功为止//TODO:上述代码
}
jUicE_g2R的个人主页,确定不点开看看?
相关文章:

【C语言精髓之指针】结构体指针(->与.两个运算符的区别)
/*** file * author jUicE_g2R(qq:3406291309)————彬(bin-必应)* 通信与信息专业大二在读 * copyright 2023.10* COPYRIGHT 原创技术笔记:转载需获得博主本人同意,且需标明转载源* language C/C* IDE Base on Mic…...

SpringCloud 微服务全栈体系(一)
第一章 认识微服务 随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢? 一、单体架构 单体架构:将业务的所有功能集中在一个项目中开发ÿ…...

Echarts自定义柱状图
目录 效果图 echarts官网找相似图 将柱状图引入html页面中 自定义柱状图 将不需要的属性删除 编辑 修改图形大小 grid 不显示x轴 编辑 不显示y轴线和相关刻度 编辑 y轴文字的颜色设置为自己想要的颜色 修改第一组柱子相关样式(条状) …...
LuatOS-SOC接口文档(air780E)-- ioqueue - io序列操作
ioqueue.init(hwtimer_id,cmd_cnt,repeat_cnt) 初始化一个io操作队列 参数 传入值类型 解释 int 硬件定时器id,默认用0,根据实际MCU确定,air105为0~5,与pwm共用,同一个通道号不能同时为pwm和ioqueue int 一个完…...
探讨Socks5代理技术的原理及其在不同领域的应用
Socks5代理:实现网络连接的智能之选 作为一种网络代理协议,Socks5代理技术通过转发网络数据包,实现了客户端和服务器之间的代理传输。其独特的特性在跨界电商、爬虫数据分析、企业出海和游戏体验等领域发挥着关键作用,为用户提供…...
sql注入的基本手法
目的 通过sqk注入获取数据内容 掌握sql注入基本手法 我们这里使用 1.联合注入 就是利用union select 语句 两条语句 同时执行 实现跨库跨表查询 条件 两条select语句查询结果具有相同列数 对应列数数据类型相同 简单的步骤 1.目标分析 ?id…...
8.1 C++ 标准输入输出流
C/C语言是一种通用的编程语言,具有高效、灵活和可移植等特点。C语言主要用于系统编程,如操作系统、编译器、数据库等;C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统、图形用户界面、嵌入式系统等。…...
hive往es映射表写数据报错
hive是基于Hadoop的一个数据仓库工具,用来进行数据提取、转化、加载,这是一种可以存储、查询和分析存储在Hadoop中的大规模数据的机制。hive数据仓库工具能将结构化的数据文件映射为一张数据库表,并提供SQL查询功能,能将SQL语句转…...

2023年【广东省安全员A证第四批(主要负责人)】考试试卷及广东省安全员A证第四批(主要负责人)模拟考试
题库来源:安全生产模拟考试一点通公众号小程序 广东省安全员A证第四批(主要负责人)考试试卷根据新广东省安全员A证第四批(主要负责人)考试大纲要求,安全生产模拟考试一点通将广东省安全员A证第四批&#x…...

YOLOv5算法改进(15)— 如何去更换Neck网络(包括代码+添加步骤+网络结构图)
前言:Hello大家好,我是小哥谈。在学习完了如何去更换主干网络之后,接着就让我们通过案例的方式去学习下如何去更换Neck网络。本篇文章的特色就是比较浅显易懂,附加了很多的网络结构图,通过结构图的形式向大家娓娓道来,希望大家学习之后能够有所收获!🌈 前期回顾: YO…...

用Nginx搭建一个具备缓存功能的反向代理服务
在同一台服务器上,使用nginx提供服务,然后使用openresty提供反向代理服务。 参考《Ubuntu 20.04使用源码安装nginx 1.14.0》安装nginx。 参考《用Nginx搭建一个可用的静态资源Web服务器》搭建静态资源Web服务器,但是/nginx/conf/nginx.conf里…...

YOLOv5改进实战 | 更换主干网络Backbone(三)之轻量化模型Shufflenetv2
前言 轻量化网络设计是一种针对移动设备等资源受限环境的深度学习模型设计方法。下面是一些常见的轻量化网络设计方法: 网络剪枝:移除神经网络中冗余的连接和参数,以达到模型压缩和加速的目的。分组卷积:将卷积操作分解为若干个较小的卷积操作,并将它们分别作用于输入的不…...
【Markdown】 Markdown 操作备忘录
To Do List 显示目前todo list 的状态 getLogger() 单例类, 通过引入模块,获取单例日志对象 结果可视化调研 模型结果保存及测试 - [ ] getLogger() 单例类, 通过引入模块,获取单例日志对象 - [ ] 结果可视化调研 - [x] 模型结果…...

【自动化测试】基于Selenium + Python的web自动化框架
一、什么是Selenium? Selenium是一个基于浏览器的自动化工具,她提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。Selenium主要包括三部分:Selenium IDE、Selenium WebDriver 和Selenium Grid: 1、Selenium IDE&…...
zookeeper连接客户端操作数据时报错Socket is not connected
文章目录 一、报错信息二、问题描述三、原因分析:四、解决方案: 一、报错信息 DEBUG org.apache.zookeeper.ClientCnxnSocketNIO - Ignoring exception during shutdown input java.net.SocketException: Socket is not connectedat sun.nio.ch.Net.tra…...

mysql select语句中from 之后跟查询语句
概念:将from后面的查询语句放在FROM的后面,则查询到的结果,就会被当成一个“表”; 这里有一个特别要注意的地方,放在FROM后面的子查询,必须要加别名。 select dui.id,dui.party_service_id mes_id, dui.party_id,dui.…...
Yolov8小目标检测(26):多尺度空洞注意力(MSDA) | 中科院一区顶刊 DilateFormer 2023.9
💡💡💡本文独家改进:多尺度空洞注意力(MSDA)采用多头的设计,在不同的头部使用不同的空洞率执行滑动窗口膨胀注意力(SWDA),全网独家首发,创新力度十足,适合科研 多尺度空洞注意力(MSDA) | 亲测在红外弱小目标检测涨点,map@0.5 从0.755提升至0.784 💡�…...

NLP:从头开始的文本矢量化方法
一、说明 NLP 项目使用文本,但机器学习算法不能使用文本,除非将其转换为数字表示。这种表示通常称为向量,它可以应用于文本的任何合理单位:单个标记、n-gram、句子、段落,甚至整个文档。 在整个语料库的统计 NLP 中&am…...
Kotlin 中 apply、let、also、run的区别
apply apply 函数接收一个目标并回来该目标自身。它答应您在目标上履行一些操作,同时仍然回来原始目标。 fun <T> T.apply(block: T.() -> Unit): TT 是目标的类型,block 是一个 lambda 表达式,能够在该目标上履行一些操作。在这个…...

Android JKS MD5 SHA1 公钥生成 私钥生成 APP备案 内容获取
1 查看 jks keytool -list -v -keystore /Users/lipengfei/Desktop/android/androidproject.jks密钥库类型: jks 密钥库提供方: SUN您的密钥库包含 1 个条目别名: ddgj 创建日期: 2018-11-16 条目类型: PrivateKeyEntry 证书链长度: 1 证书[1]: 所有者: CNcn, OUcn, Ocn, Lcn,…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
Python竞赛环境搭建全攻略
Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型(算法、数据分析、机器学习等)不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...
Docker、Wsl 打包迁移环境
电脑需要开启wsl2 可以使用wsl -v 查看当前的版本 wsl -v WSL 版本: 2.2.4.0 内核版本: 5.15.153.1-2 WSLg 版本: 1.0.61 MSRDC 版本: 1.2.5326 Direct3D 版本: 1.611.1-81528511 DXCore 版本: 10.0.2609…...