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…

基于有序抖动块截断编码的水印嵌入和提取算法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;以获得最佳的游戏体验。不同的游戏和平…

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 -> …

vscode 打代码光标特效

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

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

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

分布式监控平台---Zabbix

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

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

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

【Maven工具】

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

(三)C++自制植物大战僵尸游戏项目结构说明

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/ErelL 一、项目结构 打开项目后&#xff0c;在解决方案管理器中有五个项目&#xff0c;分别是libbox2d、libcocos2d、librecast、libSpine、PlantsVsZombies五个项目&#xff0c;除PlantsVsZombies外&#xff0c;其他四个…

【算法】快速排序的基本思想、优化 | 挖坑填补法和区间分割法

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法分析与设计知识专栏&#xff1a;算法分析&#x1f525; 给大家跳…

基于springboot+vue实现的疫情防控物资调配与管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

家居网购项目(权限验证+事务管理)

文章目录 1.过滤器权限认证1.程序框架图2.web.xml3.编写AdminAuthorization4.编写MemberAuthorization5.细节6.结果展示1.未登录可以任意浏览商品2.点击添加购物车提示登录3.点击后台管理&#xff0c;提示管理员登录4.也做了其余资源的访问验证 2.事务管理1.思路分析2.重写JDBC…

程序猿之路

我接触计算机算对自己来说是比较晚的了&#xff0c;上初中的时候就有微机课&#xff0c;但是在那个小县城&#xff0c;上课也只是3个人共用一个电脑&#xff0c;我初中整个过程只会开关机&#xff0c;哈哈&#xff0c;虽然学过word&#xff0c;但是无奈&#xff0c;我插不上手呀…

开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback

开源相机管理库Aravis例程学习&#xff08;三&#xff09;——回调multiple-acquisition-callback 简介例程代码arv_camera_create_streamArvStreamCallbackTypeArvStreamCallback 简介 本文针对官方例程中的&#xff1a;02-multiple-acquisition-callback做简单的讲解。 ara…