【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->name2-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,…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
uniapp 小程序 学习(一)
利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...
API网关Kong的鉴权与限流:高并发场景下的核心实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中,API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关,Kong凭借其插件化架构…...
Linux安全加固:从攻防视角构建系统免疫
Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...
