0601-指针的基础
内存
物理存储器和存储地址空间
物理存储器:实际存在的具体存储器芯片。比如:内存条、RAM芯片、ROM芯片。
存储地址空间:对存储器编码的范围。
- 编码:对每个物理存储单元(一个字节)分配一个号码
- 寻址:可以根据分配的号码找到相应的存储单元,完成数据的读写
内存地址
- 将内存抽象成一个很大的一维字符数组。
- 编码就是对内存的每一个字节分配一个32位或64位的编号(与32位或者64位处理器相关)。
- 这个内存编号我们称之为内存地址。
内存中的每一个数据都会分配相应的地址: - char:占一个字节分配一个地址
- int: 占四个字节分配四个地址
- float、struct、函数、数组等
指针和指针变量
-
指针就是地址,地址就是指针
int a; int b[]; int* p;
上述代码中
&a
、和b
和p
都是地址,也都是指针。 -
指针变量是存放地址的变量
-
通常我们叙述时会把指针变量简称为指针,实际他们含义并不一样
指针的基础知识
指针变量的定义和使用
定义指针的方式如下:
数据类型* 变量名
其中*
表示该变量为指针变量,数据类型表示该指针变量保存的是哪一种数据类型的地址,也就是指针变量指向哪一种数据类型。
访问指针变量指向的内存空间的数据:
*指针变量
此处的*
为取值运算符。
指针变量保存的是内存地址,*指针变量
就表示访问该地址对应的内存空间。对*指针变量
操作,其实就是读写该内存空间的数据。
#include<stdio.h>int main() {int a = 10;int* p;p = &a;printf("a = %d\n", a);printf("p指向的数据=%d\n", *p);printf("变量a的地址:%p\n", &a);printf("指针变量p存的地址:%p\n", p);return 0;
}
运行上面代码,结果如下:
注意:&
可以取得一个变量在内存中的地址。但是,不能取寄存器变量,因为寄存器变量不在内存里,而在CPU里面,所以是没有地址的。
使用指针访问数据的原理
不同类型的数据在内存中占用不同的字节数,指针变量保存的是数据首个字节的地址。系统根据指针变量保存的地址找到首个字节后,再根据指针指向的数据类型,顺位读取不同的字节长度,从而访问到完整的数据。
比如,一个int
型变量a
占4字节,指针变量int* p
只保存了a
在内存中的首个字节的地址。当操作*p
时,系统会先根据该地址找到首个字节,然后再顺位读取3个字节,总共读取了4个字节,从而取出变量a
的值。
通过指针间接修改变量的值
#include<stdio.h>int main() {int a = 10;int* p;p = &a;printf("a = %d\n", a);printf("p指向的数据=%d\n", *p);printf("变量a的地址:%p\n", &a);printf("指针变量p存的地址:%p\n", p);*p = *p + 1;printf("p指向的数据加1后,a = %d\n", a);printf("p指向的数据加1后,p指向的数据=%d\n", *p);return 0;
}
运行上面的代码,结果如下:
在定义指针类型时一定要和指向的变量类型是一样的才行。
#include<stdio.h>int main() {char a = 97;int* p = &a;printf("a的值=%d\n", a);printf("*p的值=%d\n", *p);return 0;
}
运行上面的代码,结果如下:
上面代码中,虽然指针变量p
保存的是字符变量a
的地址,但是p
是指向int型数据的,所以在找到a
的地址后还会顺位读取3个字节的数据。
指针大小
windows中数据存储采用小端对齐的方式。也就是,数据的低位放在地址小的内存中。
指针也是一种数据类型,所以可以使用sizeof()
测量指针的大小,得到的总是:4或8。
这是因为指针存储的是内存地址。在32位处理器中内存地址是一个32位的编号,在64位处理器中内存地址是一个64位的编号。
所以在32位处理器中sizeof(指针)
得到的总是4,在64位处理器中sizeof(指针)
得到的总是8。
在visio studio中可以在下图中选择基于32位或64位的处理器编译,从而使sizeof(指针)
得到不同的结果。
int main() {int* p1;int** p2;char* p3;char** p4;printf("sizeof(p1) = %d\n", sizeof(p1));printf("sizeof(p2) = %d\n", sizeof(p2));printf("sizeof(p3) = %d\n", sizeof(p3));printf("sizeof(p4) = %d\n", sizeof(p4));printf("sizeof(double *) = %d\n", sizeof(double*));return 0;
}
上述代码基于32位处理器编译运行后结果如下:
上述代码基于64位处理器编译运行后结果如下:
野指针和空指针
野指针
任意数值赋值给指针变量没有意义,因为这样的指针就成了野指针,此指针指向的区域是未知(操作系统可能不允许操作此指针指向的内存区域)。所以,野指针不会直接引发错误,操作野指针指向的内存区域可能会出问题。
int main() {int* p = 100;/*操作系统将0~255的内存空间作为系统占用,不允许被访问操作*/printf("%d\n", *p);return 0;
}
空指针
为了标志此指针变量没有指向任何变量(空闲可用),C语言中,可以把NULL
赋值给此指针,这样就标志此指针为空指针。
int *p = NULL;
NULL
是一个值为0的宏常量:
#define NULL ((void *)0)
空指针可用于条件判断
int* p = NULL;if (p == NULL) {printf("我是一个空指针\n");}else {printf("我不是一个空指针\n");}
万能指针void *
万能指针可以接收任意数据类型的地址。但不能直接访问万能指针指向的内存空间,因为系统找到数据的首个字节后,不知道接下来要顺位读取多少个字节。
必须先将万能指针强转为某个具体类型的指针变量后才可以访问。
#include<stdio.h>int main() {int a = 10;void* p = &a;printf("变量a的值:%d\n", a);//printf("万能指针p指向的值:%d\n", *p);//不能直接访问万能指针指向的内存空间printf("万能指针p指向的值:%d\n", *(int*)p);*(int*)p = *(int*)p + 1;printf("加1后,变量a的值:%d\n", a);printf("加1后,万能指针p指向的值:%d\n", *(int*)p);return 0;
}
执行上面代码,结果如下:
const修饰的指针变量
const 数据类型* 变量名
:const
修饰数据类型,说明该指针变量指向的内存空间可读不可写。
数据类型* const 变量名
:const
修饰变量名,说明该指针变量保存的内存地址可读不可写。
const 数据类型* const 变量名
:const
既修饰数据类型又修饰变量名,说明该指针变量指向的内存空间可读不可写,保存的内存地址也是可读不可写的。
#include<stdio.h>int main() {const int a = 10;//a = 20;//errint b = 20;const int* p = &a;printf("指针变量p指向的值:%d\n",*p);//*p = 30;//errp = &b;//okprintf("指针变量p指向的值:%d\n", *p);int* const p2 = &a;printf("指针变量p2指向的值:%d\n", *p2);//p2 = &b;//err*p2 = 30;//okprintf("指针变量p2指向的值:%d\n", *p2);printf("a的值:%d\n", a);const int* const p3 = &b;//*p3 = 40;//err//p3 = &a;//errprintf("指针变量p3指向的值:%d\n", *p3);b = 40;printf("指针变量p3指向的值:%d\n", *p3);return 0;
}
运行上面代码,结果如下:
const
只能限定它所修饰的指针变量,我们不能通过这个指针变量来修改指向的内存空间的数据或修改这个指针变量保存的内存地址。但是,我们能通过其它指针变量来修改它们。
#include<stdio.h>int main() {int a = 10;int b = 20;const int* p = &a;//*p = 20;//err 不能通过指针变量p改变a的值a = 30;//okprintf("a的值:%d\n", a);printf("*p的值:%d\n", *p);const int* const p2 = &a;int** p3 = &p2;//*p2 = 20;//err 不能通过指针变量p2改变a的值//p2 = &b;//err 不能改变指针变量p2的指向,即不能改变p2保存的内存地址**p3 = 40;//ok 通过指针变量p3改变a的值printf("通过指针变量p3改变a的值后,a的值:%d\n", a);printf("通过指针变量p3改变a的值后,*p2的值:%d\n", *p2);printf("通过指针变量p3改变a的值后, **p3的值:%d\n", **p3);*p3 = &b;//ok 通过指针变量p3改变指针变量p2的指向printf("通过指针变量p3改变指针变量p2的指向后,*p2的值:%d\n", *p2);printf("通过指针变量p3改变指针变量p2的指向后, **p3的值:%d\n", **p3);return 0;
}
运行上面的代码,结果如下:
相关文章:

0601-指针的基础
内存 物理存储器和存储地址空间 物理存储器:实际存在的具体存储器芯片。比如:内存条、RAM芯片、ROM芯片。 存储地址空间:对存储器编码的范围。 编码:对每个物理存储单元(一个字节)分配一个号码寻址&…...
关于K8S库中高可用的锁机制详解
简介 对于无状态的组件来说,天然具备高可用特性,无非就是多开几个副本而已;而对于有状态组件来说,实现高可用则要麻烦很多,一般来说通过选主来达到同一时刻只能有一个组件在处理业务逻辑。 在Kubernetes中,…...

常用中外文献检索网站大盘点
一、常用中文文献检索权威网站: 1、知网:是全球最大的中文数据库。提供中国学术文献、外文文献、学位论文、报纸、会议、年鉴、工具书等各类资源,并提供在线阅读和下载服务。涵盖领域包括:基础科学、文史哲、工程科技、社会科学、…...

公司招了一个00后,以为是个小年轻,没想到人家是个卷王...
公司前段缺人,也面了不少测试,结果竟然没有一个合适的。一开始瞄准的就是中级的水准,也没指望来大牛,提供的薪资也不低,面试的人很多,但平均水平很让人失望。 令我印象最深的是一个00后测试员,…...
数字化转型难?怎么转?听听厂商、CIO、CEO怎么说
数字化转型已经成为当今商业领域中的热门话题。对于许多企业来说,数字化转型是一项重要而且必不可少的战略,以适应快速变化的市场环境并保持竞争力。然而,数字化转型并不是一项容易的任务,它涉及到许多方面,需要综合考虑技术、组织和文化等因素。那么,让我们来听听一些厂…...
C++面试题汇总
C面试题汇总 1. new/delete和malloc/free:2. delete和delete[]:3. 常引用:4. overload、override、overwrite的介绍5. C是不是类型安全的?6. main 函数执行以前,还会执行什么代码?7. 数组与指针的区别&…...
OpenAi编写基于Python+OpenCV的人脸识别实现带墨镜效果
要基于Python和OpenCV实现带墨镜效果的人脸识别,你可以按照以下步骤进行操作: 安装所需的库:确保你已经安装了Python和OpenCV库。你可以使用pip命令来安装OpenCV库:pip install opencv-python。 导入必要的库:在Pytho…...
安卓闲谈吹水
一、熟练掌握 Java 语言,面向对象分析设计能力,反射原理,自定义注解及泛型,多次采用设计模式重构项目 首先我们先了解什么是对象。 1.对象是由我们自己定义的类来创建出来的。 2.对象实际上就是类的具体实现。 (对象是类的一个实…...
测试类的使用
1.在pom文件中添加依赖 <dependencies> <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>compile</scope> </dependency> </dependencies>2.在s…...
【物联网技术对生活的影响与展望】
随着科技日新月异的发展,物联网(IoT)技术正在快速地影响着我们的生活。它是将各种设备和物品连接在一起,通过互联网使它们可以相互交流和传递数据的技术。它的应用范围广泛,可以涵盖从智能家居到工业网络的各个领域。 …...
MySQL数据库函数详解及示例
以下是一份按照常见MySQL数据库函数,并且包含函数示例: 字符串函数 字符串函数用于处理和操作文本数据。 CONCAT:将多个字符串连接为一个字符串。SUBSTRING:提取字符串的一部分。LENGTH:返回字符串的长度。REPLACE&…...

ES6对象新增了哪些扩展?
一、属性的简写 ES6中,当对象键名与对应值名相等的时候,可以进行简写 const baz {foo:foo}// 等同于 const baz {foo} 方法也能够进行简写 const o {method() {return "Hello!";} };// 等同于const o {method: function() {return &qu…...

深入理解 Linux 内核
Linux 内核系列文章 Linux 内核设计与实现 深入理解 Linux 内核 深入理解 Linux 内核(二) Linux 设备驱动程序 Linux设备驱动开发详解 文章目录 Linux 内核系列文章前言一、绪论二、内存寻址1、内存地址2、硬件中的分段(1)段选择符…...
机器人科普--evoBOT
机器人科普--evoBOT 1 介绍2 视频3 Modular prototype4 Mechanical construction5 Hardware architecture and technology6 Autonomy und navigation7 Operation of the robot8 Technical data参考 1 介绍 The evolution of autonomous mobile robotic systems 2 视频 evoBOT…...

数据结构课程设计——运动会分数统计
运动会分数统计 数据结构课程设计任务书 学生姓名:xxx 专业班级:软件工程 指导教师: 工作单位: 题 目: 运动会分数统计 基础要求: 要求具有C语言的理论基础…...

C语言递归算法实现经典例题
一.递归 1.什么是递归 递归是一种编程技术,它通过在函数内部反复调用自身来解决问题。当一个程序调用自己时,这就称为递归调用。递归可以有助于简化某些算法的实现和理解。在递归过程中,每个调用都会将一些数据保存在栈上,直到递…...

ST典型碳化硅MOSFET驱动应用方案
ST典型碳化硅MOSFET驱动应用方案 1.栅极驱动器规格和功能实现 参考资料:ST官网应用手册《AN4671》 作者:Xiou 1.栅极驱动器规格和功能实现 以下是对栅极驱动要求的简短列表: dv / dt 的瞬变抗扰度:在整个温度范围内 50 V/ns。 …...
对比AMD和英特尔显卡的区别
✨求关注~ 😀博客:www.protaos.com AMD和英特尔都是著名的半导体公司,它们都生产处理器和显卡。在显卡领域,AMD生产Radeon系列显卡,而英特尔则生产Intel HD Graphics和Intel Iris Graphics系列显卡。 使用群体对比&…...
Linux系统c语言socket实现UDP通信
UDP全称 User Datagram Protocol,即:用户数据报协议。是面向无连接的协议。通常,UDP 通信还会被冠以不可靠的头衔。这里的不可靠指的是:无法可靠地得知对方是否收到数据。 UDP有如下特征: 无连接:通信双方不需要事先连接无确认:收到数据不给对方发回执确认不保证有序、丢…...

常用五大类RFID系统,实践领域广泛,加强现代化管理
随着信息技术的不断进步,RFID技术已逐渐成为企业管理及社会服务领域中不可或缺的一种重要技术手段。根据其不同的应用场景,RFID技术广泛应用于药品监管、固定资产管理、仓储管理、智慧工厂和消费服务等领域。本文将从五个方面介绍常用的RFID系统。 一、…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...