C语言之探秘:访问结构体空指针与结构体空指针的地址的区别(九十三)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长!

优质专栏:Audio工程师进阶系列原创干货持续更新中……】🚀
优质专栏:多媒体系统工程师系列原创干货持续更新中……】🚀
优质视频课程:AAOS车载系统+AOSP14系统攻城狮入门实战课原创干货持续更新中……】🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮

欢迎关注Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉

    • 🌻1.前言
    • 🌻2.C语言之结构体空指针与结构体空指针的地址介绍
    • 🌻3.代码实例
      • 🐓3.1 (frame_t *)0 等同于 frame_t *p = NULL;
      • 🐓3.2 问题一:为什么访问p->f1发生段错误?而&(p->f1)则不会?
      • 🐓3.3 问题二: (frame_t *)0 和 frame_t *p = NULL;区别?
      • 🐓3.4 问题三: 根据打印,发现&(p->f1)打印等于8,正好是f1在frame_t中的偏移,而不是地址,为什么&(p->f1)表示的是f1成员变量在frame_t的偏移,而不是地址?

🌻1.前言

本篇目的:C语言之探秘:访问结构体空指针与结构体空指针的地址的区别

🌻2.C语言之结构体空指针与结构体空指针的地址介绍

  • 在C语言中,结构体(Struct)是一种复合数据类型,它可以包含多个不同类型的数据项。指针(Pointer)是一种特殊的数据类型,用于存储变量地址。空指针(Null Pointer)是一个不指向任何有效内存地址的特殊指针。
  • 当我们谈论结构体空指针与结构体空指针的地址时,我们需要明确两个概念:结构体变量的地址和结构体指针的地址。
    1. 结构体空指针:
  • 结构体空指针是指未经初始化的结构体指针,它不指向任何有效的内存地址。在C语言中,我们可以使用malloc函数或calloc函数为结构体指针分配内存。如果没有分配内存,那么这个结构体指针就是空指针。
struct Person {char *name;int age;
};
struct Person *p = NULL; // 声明一个结构体指针,并初始化为空指针
  • 在这种情况下,p是一个空指针,它不指向任何有效的内存地址。
    1. 结构体空指针的地址:
  • 结构体空指针的地址是指结构体指针变量在内存中的存储位置。在C语言中,每个变量都有一个唯一的地址。我们可以使用&操作符来获取变量的地址。
struct Person *p;
printf("%p\n", (void*)&p); // 输出结构体指针变量的地址
  • 输出结果将是一个十六进制的地址值,表示p在内存中的位置。
  1. 结构体空指针与结构体空指针地址的区别:
  • 结构体空指针是一个不指向任何有效内存地址的特殊指针,而结构体空指针的地址是指结构体指针变量在内存中的存储位置。这两者是不同的概念,不能混淆。
struct Person *p = NULL; // 空指针
printf("%p\n", (void*)p); // 输出空指针的值,即NULL
printf("%p\n", (void*)&p); // 输出空指针变量的地址
  • 输出结果将是:
(nil)
0x7ff7bfeff8ac
  • 在这里,第一个输出表示空指针的值,即NULL。第二个输出表示空指针变量p在内存中的地址。
  • 在C语言中,结构体空指针与结构体空指针的地址是两个不同的概念。结构体空指针是一个不指向任何有效内存地址的特殊指针,而结构体空指针的地址是指结构体指针变量在内存中的存储位置。理解这两个概念有助于避免在编程过程中出现错误。

🌻3.代码实例

🐓3.1 (frame_t *)0 等同于 frame_t *p = NULL;

#include <stddef.h>
#include <stdio.h>
//#include <stddef.h>#undef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({                      \const typeof( ((type *)0)->member ) *__mptr = (ptr);	\(type *)( (char *)__mptr - offsetof(type,member) );})typedef struct frame {int num;char name;float f1;
}frame_t;int main(void)
{frame_t fra, *pf;fra.num = 1;fra.name = 'M';fra.f1 = 1.23;//v1.0 (frame_t *)0用法printf("(frame_t *)0 = %p\n",(frame_t *)0);printf("(frame_t*)0->f1 = %p\n",&(((frame_t *)0)->f1));printf("(frame_t*)1->f1 = %p\n",&(((frame_t *)1)->f1));printf("(frame_t*)2->f1 = %p\n",&(((frame_t *)2)->f1));printf("(frame_t*)3->f1 = %p\n",&(((frame_t *)3)->f1));//v2.0 frame_t *p = NULL用法.frame_t *p = NULL;printf("p = %p\n",p);//printf("p = %ld\n",p->f1);//段错误printf("p = %p\n",&(p->f1));//v3.0int aa = 1;frame_t *q = (frame_t *)&aa;printf("q = %p\n",q);printf("q = %p\n",&(q->name));return 0;
}

打印:
(frame_t )0 = (nil)
(frame_t
)0->f1 = 0x8
(frame_t*)1->f1 = 0x9
(frame_t*)2->f1 = 0xa
(frame_t*)3->f1 = 0xb
p = (nil)
p = 0x8
q = 0x7ffe13c1f4a4
q = 0x7ffe13c1f4a8

计算地址:
(frame_t*)0->f1 = 0x8 + 0 = 0x8 = 8
(frame_t*)1->f1 = 0x8 + 1 = 0x9 = 9
(frame_t*)2->f1 = 0x8 + 2 = 0xa = 10
(frame_t*)3->f1 = 0x8 + 3 = 0xb = 11
结论:
(frame_t*)0:等于指向0指针的偏移量 + 成员变量f1偏移量,等于8.
(frame_t*)0:等于指向1指针的偏移量 + 成员变量f1偏移量,等于9.
(frame_t*)1:等于指向2指针的偏移量 + 成员变量f1偏移量,等于10.
(frame_t*)2:等于指向3指针的偏移量 + 成员变量f1偏移量,等于11.

🐓3.2 问题一:为什么访问p->f1发生段错误?而&(p->f1)则不会?

答:
<1>.p->f1:打印指针 p 中 f1 成员的内容,但 p 是一个空指针,没有指向任何有效的内存。因此,访问 p->f1 会导致段错误。
<2>.&(p->f1) :打印指针 p 中 f1 成员的地址,不会导致段错误,因为它没有尝试访问 f1 的内容。

🐓3.3 问题二: (frame_t *)0 和 frame_t *p = NULL;区别?

<1>.在C语言中,frame_t *p = NULL; 将指针 p 初始化为空指针,意味着 p 指向的是一个特殊的空地址,不指向任何实际的结构体对象.访问 p 所指向的对象时,通常会导致未定义行为,可能会导致程序崩溃。
<2>.而 (frame_t *)0 是一个显式地将一个整数0转换为 frame_t 结构体指针类型的操作。这实际上将该指针设置为一个特定的地址,即空指针地址。
<3>.因此,(frame_t *)0 实际上与 frame_t *p = NULL; 的作用相同,都是将指针设置为空指针。

🐓3.4 问题三: 根据打印,发现&(p->f1)打印等于8,正好是f1在frame_t中的偏移,而不是地址,为什么&(p->f1)表示的是f1成员变量在frame_t的偏移,而不是地址?

答:
<1>.在C语言中,结构体的成员在内存中是连续存储的,因此可以通过计算偏移量来获取结构体成员的地址。
&(p->f1) 中的 & 运算符表示取地址操作,它取的是 f1 成员的地址,而不是简单的偏移量。
<2>.假设 p 指向一个有效的 frame_t 结构体对象,则 &(p->f1) 将返回 f1 成员的地址。
但是,如果 p 是空指针,则 &(p->f1) 仍然会尝试计算 f1 成员的地址,因为这是一个合法的操作,但是实际上不会返回有效的地址,因为它仅仅是一个空指针的偏移量加上 f1 成员在结构体中的偏移量。

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

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

相关文章

npm配置阿里镜像库

1、配置阿里云镜像源 #查看当前使用的镜像地址命令 npm config get registry#设置阿里镜像源 npm config set registry http://registry.npmmirror.com 这里要注意下&#xff0c;之前的镜像源地址 https://registry.npm.taobao.org/ 已经不能用了&#xff0c;这里要更改为新…

SpringCloud之LoadBalancer负载均衡器的简单使用

SpringCloud之LoadBalancer负载均衡器的简单使用 loadbalancer用于对提供服务的集群做一个节点的选取规则。 如图所示&#xff0c;load balancer集成在调用方 示例 创建loadbalance-base模块,并引入相关依赖 <dependencies><dependency><groupId>org.spr…

【玩转PGSQL】基础管理防火墙及用户权限管理-知否知否

基础管理 登录方式 psql -d postgres -h 127.0.0.1 -p 5432 -U postgre-d 库 -h IP -p 端口 -U 用户基础sql -- 查看所有库 \l-- 创建库 create database test;-- 进入库 \c test-- 查看所有表 \d-- 查看表结构 \d t1;-- 查看所有用户 \du-- 列显示 \x-- 显示当前库下schem…

基于有序抖动块截断编码的水印嵌入和提取算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 噪声测试 旋转测试 压缩测试 2.算法运行软件版本 matlab2022a 3.部分核心程序 ............................................................…

STM32—外部中断_按键控制 LED

目录 1 、 电路构成及原理图 2 、编写实现代码 main.c exti.c 3、代码讲解 4、烧录到开发板调试、验证代码 5、检验效果 开发板介绍 相关笔记 此笔记基于朗峰 STM32F103 系列全集成开发板的记录 1 、 电路构成及原理图 外部中断---EXTI EXTI&#xff08;External…

codeforce #925 (div3) 题解

D. Divisible Pairs 给出数组 a a a&#xff0c;如果二元组 ( i , j ) (i,j) (i,j)满足 a i a j m o d x 0 & & a i − a j m o d y 0 a_i a_j mod x 0 \&\& a_i - a_j mod y 0 ai​aj​modx0&&ai​−aj​mody0&#xff0c;则beauty。其中 i &…

BEVFormer代码阅读

1. 代码地址 https://github.com/fundamentalvision/BEVFormer 2. 代码结构 个人理解&#xff0c;代码库中的代码与两篇论文都略有不同&#xff0c;总结起来&#xff0c;其结构如下。 3. BEVFormer 的 Pipeline 根据自己调试算法模型以及对论文的理解&#xff0c;我这里将 …

牛客网刷题 :BC50 你是天才吗

描述 据说智商140以上者称为天才&#xff0c;KiKi想知道他自己是不是天才&#xff0c;请帮他编程判断。输入一个整数表示一个人的智商&#xff0c;如果大于等于140&#xff0c;则表明他是一个天才&#xff0c;输出“Genius”。 输入描述&#xff1a; 多组输入&#xff0c;每…

(十)C++自制植物大战僵尸游戏设置功能实现

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/m0EtD 游戏设置 游戏设置功能是一个允许玩家根据个人喜好和设备性能来调整游戏各项参数的重要工具。游戏设置功能是为了让玩家能够根据自己的需求和设备性能来调整游戏&#xff0c;以获得最佳的游戏体验。不同的游戏和平…

音视频、网络带宽等常用概念详解

1.aac音频参数解释 AAC帧大小为1024个sample&#xff1a;指AAC编码一般以1024个采样为一个音频帧。 采样率48khz&#xff1a;指1秒&#xff08;即1000毫秒&#xff09;采集48000次。 计算1秒有多少帧&#xff1a;48000 / 1024 46.875 a&#xff08;用a表示计算结果&#xff0…

vite - WebAssembly入门

1. 初始化 vite 项目 1.1 安装 nvm&#xff08;可选&#xff09; brew update brew install nvm在 ~/.zshrc 添加 export NVM_DIR~/.nvm source $(brew --prefix nvm)/nvm.sh执行如下命令 source ~/.zshrc1.2 安装 node nvm install nodenvm ls -> …

PyTorch Scheduler动态调整学习率

文章目录 PyTorch动态调整学习率1.使用官方scheduler2.自定义scheduler参考 PyTorch动态调整学习率 深度学习中长久以来一直存在一个令人困扰的问题&#xff0c;那就是如何选择适当的学习率。如果学习速率设置得过小&#xff0c;会导致模型收敛速度缓慢&#xff0c;训练时间延…

vscode 打代码光标特效

vscode 打代码光标特效 在设置里面找到settings 进入之后在代码最下方加入此代码 "explorer.confirmDelete": false,"powermode.enabled": true, //启动"powermode.presets": "fireworks", // 火花效果// particles、 simple-rift、e…

Day13-C++基础之文件操作

文件操作 #include<iostream> #include<fstream> #include<string> using namespace std; ​ class Person{ public:char m_Name[64];int m_Age; }; ​ int main(){//文本文件操作 ​//写文件//1.包含头文件 fstream//2.创建流对象ofstream ofs;//3.指定打开…

如何利用OceanBase v4.2的 外部表简化外部数据处理

为什么需要使用外表 在日常的业务场景中&#xff0c;经常遇到需要在数据库中处理外部数据的情况&#xff0c;这些数据可能来源于应用程序&#xff0c;或者是其他业务系统。一般来说&#xff0c;常是通过ETL工具将外部数据库的数据导入到数据库内部的表中&#xff0c;再进行分析…

EasyRecovery数据恢复软件好不好用?值不值得下载

EasyRecovery数据恢复软件是一款专业且功能强大的数据恢复工具&#xff0c;它旨在帮助用户从各种存储设备中恢复由于各种原因&#xff08;如误删除、格式化、病毒攻击、系统崩溃等&#xff09;导致丢失的数据。这款软件支持多种存储介质&#xff0c;包括但不限于硬盘驱动器、U盘…

分布式监控平台---Zabbix

一、Zabbix概述 作为一个运维&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数据去了解上线发布的结果&#xff0c;和网站的健康状态。 利用一个优秀的监控软件&#xff0c;我们可以&#xff1a; 通过一个友好的界面进行浏览整个…

go结构体嵌套递归调用的2种方式--struct和func

1: 需要嵌套部分是结构体类型的&#xff0c;例如&#xff1a;快照 套 子快照&#xff0c;但是子快照长得和快照一样&#xff08;同一份结构体&#xff09;&#xff0c;可以无限递归套娃&#x1fa86; // ResRootSnapshot ... type ResRootSnapshot struct {// 快照标识Urn str…

Java实现单点登录(SSO)详解:从理论到实践

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起进步&am…

【Maven工具】

maven Maven是一个主要用于Java项目的构建自动化工具。它有助于管理构建过程&#xff0c;包括编译源代码、运行测试、将编译后的代码打包成JAR文件以及管理依赖项。Maven使用项目对象模型&#xff08;POM&#xff09;文件来描述项目配置和依赖关系。 Maven通过提供标准的项目…