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.0
  int 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 成员在结构体中的偏移量。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

华为HarmonyOS 4.2公测升级计划扩展至15款新机型

华为近日宣布&#xff0c;HarmonyOS 4.2操作系统的公测升级计划将扩展到包括华为P50系列在内的15款设备。这一更新旨在为用户提供更优化的系统性能和增强的功能。 参与此次公测的机型包括华为P50、华为P50 Pro及其典藏版、华为P50E、华为P50 Pocket及其艺术定制版、华为nova系…

学习STM32第十四天

软件SPI读写W25Q64 一、简介 对W25Q64模块进行读写操作时&#xff0c;输出引脚配置为推挽输出&#xff0c;输入引脚配置为浮空或上拉输入。时钟、主机输出和片选都是输出引脚&#xff0c;主机输入是输入引脚。SPI协议是通过命令和数据进行通信&#xff0c;在硬件中使用移位寄…

将自己的项目上传至Git

一、安装Git 官网:Git (git-scm.com) 二、注册gitee 官网:工作台 - Gitee.com 进入“我的”出现以下界面 三、创建仓库 点击加号&#xff0c;新建仓库 根据自己的需求取名&#xff0c;描述仓库&#xff0c;开源还是私有&#xff0c;点击创建即可&#xff0c;点击我的即可…

每日一题 — 找到字符串中所有字母异位词

解法一&#xff1a;暴力枚举 解法二&#xff1a;滑动窗口hash表优化 定义left和right为起始坐标0&#xff0c;right向后遍历&#xff0c;并加入到哈希表中&#xff0c;然后也要记录下来每次进入哈希表的有效字符&#xff08;与目标字符串中相同的字符&#xff09;的个数且这个滑…

C++修炼之路之list模拟实现--C++中的双向循环链表

目录 引言 一&#xff1a;STL源代码中关于list的成员变量的介绍 二&#xff1a;模拟实现list 1.基本结构 2.普通迭代器 const迭代器的结合 3.构造拷贝构造析构赋值重载 清空 4.inserterase头尾插入删除 5.打印不同数据类型的数据《使用模板加容器来完成》 三&#xf…

SQLite、MySQL 和 PostgreSQL 数据库速度比较(本文阐述时间很早比较,不具有最新参考性)(二十五)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;用于 SQLite 的异步 I/O 模块&#xff08;二十四&#xff09; 下一篇&#xff1a;SQLite—系列文章目录 注意&#xff1a;本文档非常非常旧。它描述了速度比较 SQLite、MySQL 和 PostgreSQL 的古老版本。 这里…

学习一门语言的方法和套路(B站转述)

视频链接 up虽然长相英(ping)俊(ping)&#xff0c;但是讲的干活&#xff0c;没恰饭。 学习流程&#xff1a; 1.快速阅读&#xff0c;掌握概况 2.深入细节内容 例如&#xff1a;java (JDBC)、html 、netty 不管三七二十一&#xff0c;先了解套路&#xff0c;再深入研究。 高…

安装CUDNN详细过程

cuDNN&#xff08;CUDA Deep Neural Network library&#xff09;是由NVIDIA开发的深度学习GPU加速库。 cuDNN包含了许多针对神经网络操作进行高度优化的函数&#xff0c;旨在使深度学习框架能够在NVIDIA的GPU上实现最佳性能&#xff0c;这个库提供了高效计算和加速&#xff0c…

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

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

在 PyCharm 中使用系统安装的 Python 和 Anaconda 的 Python什么区别

virtualenv environment &#xff1a; virtualenv 是一个用于创建独立 Python 环境的工具。它可以在同一个系统上创建多个相互独立的 Python 环境&#xff0c;每个环境都有自己的 Python 解释器和包库&#xff0c;从而可以实现不同项目之间的依赖隔离和版本控制。coda environm…

vue快速入门(二十五)本地存储与初始化使用

注释很详细&#xff0c;直接上代码 上一篇 新增内容 本地获取数据数据存储到本地 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial…

2024蓝桥杯——宝石问题

先展示题目 声明 以下代码仅是我的个人看法&#xff0c;在自己考试过程中的优化版&#xff0c;本人考试就踩了很多坑&#xff0c;我会—一列举出来。代码可能很多&#xff0c;但是总体时间复杂度不高只有0(N) 函数里面的动态数组我没有写开辟判断和free&#xff0c;这里我忽略…

频率域滤波基础(离散傅里叶变换使用填充的缺陷)

本来是个很简单的问题&#xff0c;作者硬是写的这么复杂&#xff0c;翻译还搞错了。重点是我发现作者真正有用的东西没讲到&#xff0c;比如相位和谱如何影响图像。连个转换公式都没有&#xff0c;我只能说作者是在混字数。 首先看关于中心对称是什么意思&#xff1f;我木太明白…

MySql 视图 存储过程 触发器

文章目录 视图数据库对象视图的理解创建、查看、更新、删除 存储过程和存储函数概述分类存储过程的创建和调用存储函数的创建和调用存储过程和存储函数的对比存储过程和存储函数的查看、修改、删除 变量GLOBAL 与 SESSION 变量的使用会话用户变量和局部变量的使用 定义条件与处…

【机器学习300问】70、向量化技术来计算神经网络时维度如何确保正确?

一、向量化技术在进行神经网络计算时的优势 向量化是一种优化技术&#xff0c;通过使用数组操作代替for循环&#xff0c;可以大大提高代码的性能和效率。在深度学习中尤其明显&#xff0c;可以提高计算效率、简化代码、优化内存使用。 二、如何确保计算时维度是正确的&#xf…

中标了,Trojan/Hijack.v木马病毒怎么解决?

火绒只是提示有病毒木马&#xff0c;并未解决。 经过不断尝试.。。。。。。 往下拉找到 Internet选项 连接 – 局域网设置 把前面的勾选取消 发现以上办法网络上出现的搜索注册表关键字等办法都无法解决。。。 解决方法一&#xff1a; 电脑进入安全模式&#xff0c;然后进…

【vue】v-model 双向数据绑定

:value&#xff1a;单向数据绑定v-model&#xff1a;双向数据绑定 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">…

STM32 MPU配置参数

TXE LEVEL一般只用MPU_TEX_LEVEL0 1 - 1 - 1 -0性能最强&#xff08;TEX - C - B- S&#xff09;. #define MPU_TEX_LEVEL0 ((uint8_t)0x00) #define MPU_TEX_LEVEL1 ((uint8_t)0x01) #define MPU_TEX_LEVEL2 ((uint8_t)0x02) 基于上表进行常用配置 &#xff…

Wpf 使用 Prism 实战开发Day19

待办事项功能页面完善以及优化 概要&#xff1a; 由于待办事项功能页&#xff0c;数据已正常渲染出来了。但页面新增&#xff0c;查询&#xff0c;修改&#xff0c;删除等功能还未实现。本章节来实现页面的请求后台实现CURD&#xff08;增删改查&#xff09; 一.待办事项查询…

泰迪智能科技携手韩山师范学院“企业微专业合作办学招生宣讲”圆满结束

为进一步深化校企合作&#xff0c;落实高校应用型人才培养。2024年4月11日&#xff0c;泰迪智能科技携手韩山师范学院开展企业微专业合作办学招生宣讲会在韩山师范学院顺利举行&#xff0c;本次宣讲会旨在与韩山师范学院学子深入讲解数字经济时代下的企业用工需求&#xff0c;着…