【指针的深刻理解】
如何看待下面代码中的a变量?
#include<stdio.h>
int main()
{int a = 0;//同样的一个a,在不同的表达式中,名称是一样的,但是含义是完全不同的!a = 10;//使用的是a的空间:左值int b = a; //使用的是a的内容:右值return 0;
}
在C语言中,表达式的值可以分为左值和右值。
左值指的是可以出现在赋值语句的左边的表达式,它代表一个可修改的内存地址。比如变量名、数组名、指针等,它们都可以被赋值,因此是左值。例如,`a`、`b`、`array[0]`、`&x`等都是左值。
右值指的是可以出现在赋值语句的右边的表达式,它代表一个常量或一个值。比如字面量、函数返回值等都是右值。右值通常不能被赋值,因此不能出现在赋值语句的左边。例如,`10`、`"hello"`、`x + y`、`func()`等都是右值。
结论:同样一个a变量,在不同的应用场景中,a本身的含义是不同的。
1.重新理解变量。
定义一个变量,本质是在内存中根据类型来进行开辟空间。有了空间,就必须具有地址来标识空间,来方便CPU进行寻址。有了空间,就可以把数据保存起来。所以,目前我们先讨论变量的空间和内容这两个概念。
2. 什么是指针?
指针就是地址!那么地址本质是什么呢?地址是数据,那么数据可不可以被保存在变量空间里面呢?当然可以。
3. 有没有指针变量这个概念?
保存指针(地址)数据的变量就叫做指针变量。
4. 指针和指针变量又有何不同?我们口语中的"定义一个指针"究竟是什么意思?我们该如何理解这种说法?
严格意义上,指针和指针变量是不同的,指针就是地址值,而指针变量是C中的变量,要在特定区域开辟空间,要用来保存地址数据,还可以被取地址。(先分开) 但是,我们经常在口语化表达的时候,又经常将这两个概念混合,具体原因无从考证,不过个人认为与最早的C资料(书, 文档之类)的翻译有关。然后,书与书之间互相借鉴,形成了这样的说法。
#include<stdio.h>
int main()
{int* p = NULL;//指针就是地址//指针变量本质就是变量,然后里面保存的是地址(指针)值//指针变量:空间(左值)+内容(右值:地址)p = (int*)0x1234;//p变量的空间:左值int* q = p;//p变量的内容:右值,就是刚刚的0x00001234,此时指针==指针变量(int*)0x11223344;//指针?还是指针变量? --- 指针10;//整数10?还是整数变量? --- 整数10return 0;
}
小练习一下
#include<stdio.h>
int main()
{int a = 10;int* p = &a;p = 10; //什么意思? --- p指的是空间:左值 --- p-->指针变量int* q = p; //什么意思? --- p指的是内容:右值 --- p-->指针*p = 10; //什么意思?--- *p指的是空间:左值 --- *p-->整型变量int b = *p; //什么意思? --- *p指的是内容:右值 --- *p-->整型值return 0;
}
结论:指针就是地址,指针变量是一个变量,变量内部保存指针(地址)数据。
为什么要有指针?
为何每间宿舍都要有门牌号呢?
门牌号可以帮助学生快速准确地找到自己的宿舍,能够极大地提高查找效率。
类比到计算机中
- CPU在内存中寻址的基本单位是多大? --- 字节
- 在32位机器下,最多能够识别多大的物理内存?--- 32位地址总线最多只能寻址2的32次方个不同的地址,而每个地址对应一个字节,因此可识别的物理内存总大小为2的32次方字节,即4GB。
- 既然CPU寻址按照字节寻址,但是内存又很大,所以,内存可以看做众多字节的集合
其中,每个内存字节空间,相当于一个学生宿舍,字节空间里面能放8个比特位,就好比同学们住的八人间,每个人是一个比特位。 每间宿舍都有门牌号就等价于每个字节空间对应的地址,即该空间对应的指针。
那么,为何要存在指针呢?
为了CPU寻址的效率。
如果没有,该怎么找在字节空间中的数据呢?
CPU只能遍历内存寻找数据。
#include<stdio.h>
int main()
{*((int*)0x11223344) = 10;//不方便//随后指针变量诞生了int* p = (int*)0x11223344;*p = 10;//简洁明了return 0;
}
究竟该如何理解编址
- 首先,必须理解,计算机内是有很多的硬件单元,而硬件单元是要互相协同工作的。所谓的协同,至少相互之间要能够进行数据传递。
- 但是硬件与硬件之间是互相独立的,那么如何通信呢?答案很简单,用"线"连起来。
- 而CPU和内存之间也是有大量的数据交互的,所以,两者必须也用线连起来。
- 不过,我们今天关心一组线,叫做地址总线。
- CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而因为内存中字节很多,所以需要给内存进行编址(就如同宿舍很多,需要给宿舍编号一样)
- 计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的。
- 钢琴 吉他 上面没有写上“都瑞咪发嗦啦”这样的信息,但演奏者照样能够准确找到每一个琴弦的每一个位置,这是为何?因为制造商已经在乐器硬件层面上设计好了,并且所有的演奏者都知道。本质是一种约定出来的共识!
- 硬件编址也是如此
- 我们可以简单理解,32位机器有32根地址总线,每根线只有两态,表示0,1【电脉冲有无】,那么一根线,就能表示2中含义,2根线就能表示4中含义,依次类推。32根地址线,就能表示2^32中含义,每一种含义都代表一个地址。
- 地址信息被下达给内存,在内存内部,就可以找到改地址对应的数据,将数据在通过数据总线传入CPU内寄存器。
指针的内存布局
#include<stdio.h>
int main()
{int a = 0xaabbccdd;int* p = &a;return 0;
}
- 这里定义了几个变量?在哪里定义的?
- 一个整形,有4个字节,那么应该有4个地址!那么&a取了哪一个地址?那么如何全部访问这4个字节呢?
- 如何正确的画出指针指向图?
- 这里定义了两个变量:一个整型变量a和一个指向整型变量的指针变量p。这两个变量定义在main函数的局部作用域中。
- 变量a在定义时被赋值为0xaabbccdd,占用4个字节的内存空间。指针变量p被定义为一个指向整型变量的指针,并被初始化为a的地址。因此,p指向了变量a的地址,即p所指向的内存单元存储了变量a的值0xaabbccdd。由于a占用4个字节的内存空间,因此可以通过指针p访问a的每一个字节。例如,使用*(p+0)访问a的第一个字节,使用*(p+1)访问a的第二个字节,以此类推,最终可以访问到a的全部4个字节。
- 这个图展示了指针p指向变量a的地址,即指针p存储了变量a的地址0xaabbccdd。
指针解引用
#include<stdio.h>
int main()
{int a = 10;int* p = &a;int b = *p;*p = 20;return 0;
}
- *p完整理解是,取出p中的地址,访问该地址指向的内存单元(空间或者内容)(其实通过指针变量访问,本质是一种间接 寻址的方式)
- 口诀:对指针解引用,就是指针指向的目标。所以*p,就是a。
那在int b = *p;中我们知道*是一个操纵符,*p就是一个表达式,那么此时的p是左值还是右值呢???
#include<stdio.h>
int main()
{*(double*)0 = 10.0;double* p = NULL;*p = 10.0;return 0;
}
结论:p指的是内容:右值
*p = NULL 和 p = NULL的区别
- `*p = NULL` 表示将指针p所指向的内存单元的值设置为NULL。这样做有时候可以用来释放指针所指向的内存,或者表示指针不再指向任何有效的内存地址。
- 而 `p = NULL` 表示将指针p本身的值设置为NULL。这种情况下,指针p不再指向任何有效的内存地址。
所以,两者的区别在于作用对象不同。
- `*p = NULL` 是对指针p所指向的内存单元进行操作,而`p = NULL`是对指针p本身进行操作。
如何将数值存储到指定的内存地址?
- 知道了指针的本质就是地址,地址就是数据,那么我们可以直接通过地址数据对变量进行访问吗?
- 大部分技术书,一定是落后于行业的。这本书也是,目前主流的编译器和操作系统,为了安全,已经有了很多内存保护的机制。我们目前的win和Linux都有栈随机化这样的机制来方式黑客对用户数据地址进行预测。
- 经过试验,目前vs2013和Centos7上,使用C语言定义的局部变量,在每次运行的时候,地址都是不同的。经过试验发现, 定义全局变量,每次更改代码,地址也会发生变化。所以这个实验没法正确做出来,但是程序崩溃,也能说明问题。
何为栈随机化
局部变量(在栈上开辟)在每次创建的时候其地址是随机变化的。
#include<stdio.h>
int main()
{int a = 10;//假设a变量的地址是0x12345678,访问a变量,还可以直接通过指针方式进行访问printf("%d\n", *(int*)0x12345678); //本质是一种直接寻址的方式*(int*)0x12345678 = 100; //本质是一种直接寻址的方式int* p = &a;*p = 100;//所以,C语言通过 int*p = &a;这种指针变量的间接寻址方式,访问目标数据有什么好处呢?//不用关心a变量的地址是什么,只需知道p变量里面放的是a的地址就行return 0;
}
编译器的bug
#include<stdio.h>
int main()
{int* p = NULL;p = (int*)&p;*p = 10;p = (int*)20;return 0;
}
这是一段 C 代码,它的主要作用是演示指针的使用和指针类型的转换。
- int* p = NULL;
- p = (int*)&p;
这两行代码定义一个指向整型变量的指针 p,并将它初始化为 NULL,然后将指针 p 的地址强制转换为一个指向指针变量的指针,并将转换结果赋值给 p。这样做的效果是将 p 指向自己的地址。
- *p = 10;
这一行代码将指针 p 的值设置为 10。由于 p 指向的是自己的地址,因此这个操作相当于让程序尝试修改自己的指针地址中存储的值。这是一种非常危险的行为,可能会导致程序崩溃或者出现其他严重问题。
- p = (int*)20;
这一行代码将指针 p 的值设置为 20。由于指针 p 指向的地址已经被改变,因此原来存储在 p 中的指针地址会丢失,不再指向之前的变量。
总的来说,这段代码是一种不安全的探索指针和类型转换的方式,不应该在实际的程序中使用。
相关文章:

【指针的深刻理解】
如何看待下面代码中的a变量? #include<stdio.h> int main() {int a 0;//同样的一个a,在不同的表达式中,名称是一样的,但是含义是完全不同的!a 10;//使用的是a的空间:左值int b a; //使用的是a的内容&#x…...

lintcode-图的拓扑排序(java)
拓扑排序 拓扑排序-lintcode原题题目介绍解题思路代码演示解题方法二 (参考,不用掌握)前置知识 图的拓扑序和深度优先遍历和广度优先遍历 拓扑排序-lintcode原题 127.拓扑排序-原题链接,可以点进去测试 题目介绍 描述 给定一个有向图,图节点的拓扑排序定义如下: 对…...

【状态估计】基于随机方法优化PMU优化配置(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
Rinne Loves Graph
Rinne Loves Graph (nowcoder.com) 链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 题目描述 Island 发生了一场暴乱!现在 Rinne 要和 Setsuna 立马到地上世界去。 众所周知:Island 是有一些奇怪的城镇和道路构成的…...

第15章:索引的数据结构
一、为什么使用索引 1.索引是存储引擎用于快速找到记录的一种数据结构。相当于一本书的目录。在进行数据查找时,首先查看查询条件是否命中某条索引,符合则通过索引查找相关数据。如果不符合则需要全表扫描,一条一条查找记录,直到…...

机械师曙光16电脑开机自动蓝屏怎么解决?
机械师曙光16电脑开机自动蓝屏怎么解决?有的用户在使用机械师曙光16电脑的时候,遇到了一些系统问题,导致自己无法正常的开机使用电脑。因为电脑总会变成蓝屏,无法进行任何操作。那么这个情况怎么去进行问题的解决呢?来…...

机器学习_Lasso回归_ElasticNet回归_PolynomialFeatures算法介绍_02---人工智能工作笔记0037
Lasso回归用的是L1正则化可以看到,这里的alpha就是这里的alpha对吧,就是 L1的权重 然后对于ElasticNet回归来说,这里的alpha可以看到是L1权重的超参数对吧,然后这里的p,表示的是 L2正则里面的(1-p)这里 这里要提一下: L1和L2为什么能防止过拟合,它们有什么区别?通过添加…...

第五篇:强化学习基础之马尔科夫决策过程
你好,我是zhenguo(郭震) 今天总结强化学习第五篇:马尔科夫决策过程 基础 马尔科夫决策过程(MDP)是强化学习的基础之一。下面统一称为:MDP MDP提供了描述序贯决策问题的数学框架。 它将决策问题建模为: 状态…...
Oracle面试题
1. 什么是存储过程,使用存储过程的好处? 存储过程(Stored Procedure )是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数&#…...

用Vue写教务系统学生管理
文章目录 一.首先创建新的Demo二.在APP里面绑定DemoStudent三.源码附上四.效果图(新增记录还未实现) 一.首先创建新的Demo 二.在APP里面绑定DemoStudent <template><img alt"Vue logo" src"./assets/logo.png"><!--…...

专门用于管理企业与自己客户之间所有信息的客户管理系统
一、开源项目简介 关于 NXCRM NXCRM 是一套基于 Laravel 的 CRM 应用程序。它包含了一个管理中心,可以管理用户、客户、产品、订单、商机,合同,收款,附件,联系人,跟进动态,发票,业…...

(转载)基于多层编码遗传算法的车间调度算法(matlab实现)
以下内容大部分来源于《MATLAB智能算法30个案例分析》,仅为学习交流所用。 1 理论基础 遗传算法具有较强的问题求解能力,能够解决非线性优化问题。遗传算法中的每个染色体表示问题中的一个潜在最优解,对于简单的问题来说,染色体…...

Redis的常用数据结构之哈希类型
首先这里说的哈希类型针对的是redis中的value的k-v结构 常见的操作命令 hset设置值 hsetnx命令,不存在可以设置,存在设置不成功 hget取值,这里与字符串类型不同是要精确到filed。前面的判断也是基于field来实现的 要是field没有就返回null h…...

计算机组成原理-存储系统-缓存存储器(Cache)
目录 一、Cache基本概念 1.2性能分析 二、 Cache和主存的映射发生 2.1全相连映射编辑 2.2直接映射编辑 2.3组相连映射 三、Cachae的替换算法 3.1 随机算法(RADN) 3.2 先进先出算法(FIFO) 3.3 近期最少使用(LRU) 3.4 最近不经常使用(LFU) 四、写策略 4…...
打开c语言生成exe文件,出现闪退的解决方法
为什么打开c语言生成的exe文件,立马闪退。 起初个别问的时候,我只是简单的说明程序运行完了,就自动关了, 首先,生成的exe文件本质是控制台程序,这些都是依赖于windows的控制台窗口,程序执行完…...

算法基础学习笔记——⑩DFS与BFS\树与图
✨博主:命运之光 ✨专栏:算法基础学习 目录 DFS与BFS\树与图 ✨DFS ✨BFS 🍓宽搜流程图如下: 🍓宽搜流程: 🍓广搜模板 ✨树与图 🍓树是特殊的图(连通无环的图&am…...

chatgpt赋能python:Python中可迭代对象的介绍
Python中可迭代对象的介绍 Python是一种高级编程语言,它具有简单易学、可读性强、功能强大等特点,成为了数据科学、机器学习、Web开发等领域的热门选择。Python中有很多重要的概念和功能,其中之一就是支持可迭代对象的概念。 在Python中&am…...

报表控件FastReport使用指南——如何打开WebP格式的图片
FastReport 是功能齐全的报表控件,可以帮助开发者可以快速并高效地为.NET,VCL,COM,ActiveX应用程序添加报表支持,由于其独特的编程原则,现在已经成为了Delphi平台最优秀的报表控件,支持将编程开…...

【鲁棒、状态估计】用于电力系统动态状态估计的鲁棒迭代扩展卡尔曼滤波器研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

整理6个超好用的在线编辑器!
随着 Web 开发对图像可扩展性、响应性、交互性和可编程性的需求增加,SVG 图形成为最适合 Web 开发的图像格式之一。它因文件小、可压缩性强并且无论如何放大或缩小,图像都不会失真而受到欢迎。然而,为了编辑 SVG 图像,需要使用 SV…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...