【指针的深刻理解】

如何看待下面代码中的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?还是整数变量? --- 整数10
	return 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;
}
  1. 这里定义了几个变量?在哪里定义的? 
  2. 一个整形,有4个字节,那么应该有4个地址!那么&a取了哪一个地址?那么如何全部访问这4个字节呢?
  3. 如何正确的画出指针指向图?
  • 这里定义了两个变量:一个整型变量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/23066.html

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

相关文章

PLC/DCS系统中电磁干扰的来源及解决办法

自动化系统中所使用的各种类型DCS/PLC等自动化设备&#xff0c;有的是集中安装在控制室&#xff0c;有的是安装在生产现场和各种电机设备上&#xff0c;它们大多处在强电电路和强电设备所形成的恶劣电磁环境中。要提高这类控制系统可靠性&#xff0c;必须消除各种干扰才能有效保…

Fourier分析入门——第6章——连续函数的Fourier分析

目录 第 6 章 连续函数的Fourier分析 6.1 引言 6.2 Fourier模型 6.3 求取Fourier系数的实用方法 6.4 相关定理 6.4.1 线性定理(linearity) 6.4.2 平移定理(Shift theorem) 6.4.3 伸缩定理(Scaling theorem) 6.4.4 微分定理(Differentiation theorem) 6.4.5 积分定理…

Spring源码阅读:Spring事务传播特性

一、概述 我们平常工作中经常会听到事务的传播级别&#xff0c;但是使用中基本不会太调整这个事务传播级别&#xff0c;因为没什么业务场景需要我们这么做&#xff0c;只需要使用原有的事务传播级别即可解决95%的业务场景。但是为了那5%的业务场景&#xff0c;我们还是还要学习…

完美解决接口测试难题,数据驱动带签名混合封装框架实现

目录 前言&#xff1a; 一、框架概述 二、框架架构 三、代码实现 四、实战步骤 五、总结 前言&#xff1a; 接口自动化测试是保障软件质量的重要手段之一&#xff0c;其自动化程度越高&#xff0c;越能有效提高软件测试效率。而接口自动化测试中&#xff0c;接口测试框架…

Springboot +spring security,解决跨域问题

一.简介 这篇文章主要是解释什么是跨域&#xff0c;在Spring中如何解决跨域&#xff0c;引入Spring Security后Spring解决跨域的方式失效&#xff0c;Spring Security 如何解决跨域的问题。 二.什么是跨域 跨域的概率&#xff1a; 浏览器不能执行其他网站的脚本&#xff0c…

【新星计划-2023】TCP/IP协议讲解

相信大家在学习的过程中一定听到过TCP/IP这个协议&#xff0c;那么&#xff0c;TCP/IP协议是什么&#xff1f;为什么会有TCP/IP协议&#xff1f; 一、TCP/IP是什么&#xff1f; TCP/IP是用于计算机通信的一组协议&#xff0c;我们通常称它为TCP/IP协议族。它是70年代中期美国…

Taro 项目怎么获取元素的高度和视口高度

最近做小程序&#xff0c;用的Taro&#xff0c;需要设置空状态居中显示&#xff0c;因为空状态出现的地方不一样&#xff0c;所以需要动态设置&#xff0c;那么就需要获取元素的高度来计算 文档翻了一遍也没有找到&#xff0c;原生js获取高度得到的是null&#xff0c;百度了下…

考研数据结构--图

文章目录 图图的基本概念图的定义种类 图的抽象数据类型图的基本术语1. 端点和邻接点2. 顶点的度、入度和出度3. 完全图4. 稠密图、稀疏图5. 子图6. 路径和路径长度7. 回路或环8. 连通、连通图和连通分量9. 强连通图和强连通分量在一个图中找强连通分量的方法 10. 权和网 图的存…

自信裸辞:一晃 ,失业都3个月了.....

最近&#xff0c;找了很多软测行业的朋友聊天、吃饭 &#xff0c;了解了一些很意外的现状 。 我一直觉得他们技术非常不错&#xff0c;也走的测开/管理的路径&#xff1b;二三月份裸辞的&#xff0c;然后一直在找工作&#xff0c;现在还没找到工作 。 经过我的分析&#xff0…

2023年广东省中职网络安全Web渗透测试解析(超详细)

一、竞赛时间 180分钟 共计3小时 二、竞赛阶段 1.访问地址http://靶机IP/task1,分析页面内容,获取flag值,Flag格式为flag{xxx}; 2.访问地址http://靶机IP/task2,访问登录页面。用户user01的密码为1-1000以内的数,获取用户user01的密码,将密码作为Flag进行提交,Flag格式…

如何真正开启docker远程访问2375

注意看官方文档 Configure remote access for Docker daemon | Docker Documentation 1. windows上Docker Desktop开启远程访问端口2375 系统版本&#xff1a; win10专业版 Docker Desktop版本&#xff1a;4.18.0 很简单勾上&#xff0c; 应用并重启即可 2. linux上开启 尝…

设计模式—“接口隔离”

在组件构建过程中,某些接口之间直接的依赖常常会带来很多问题、甚至根本无法实现。采样添加一层间接(稳定)接口,来隔离本来互相紧密关联的接口是一种常见的解决方案。 典型模式有:Fascade、Proxy、Adapter、Mediator 一、Fascade 动机 上述A方案的问题在于组件的客户和…

性能测试重要知识与TPS上不去原因分析,测试进阶之路卷起来...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 常见性能测试术语…

音视频使用qt测试ffmpeg接口时无法运行

仅仅时把自己过程中遇到的稍微阻塞疑惑问题做出整理&#xff0c;疑惑的是拿到的ffmpeg包中没有dll文件&#xff0c;导致自己研究了一系列。 使用qt加载音视频ffmpeg对应的相关lib库&#xff0c;进行接口&#xff0c;源码的研究。 1&#xff1a;使用源码安装的方式获取相关的动…

易基因:全基因组DNA甲基化分析揭示DNMT1在斑马鱼模型听觉系统发育中的作用 | 胚胎发育

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 听力障碍通常与内耳发育不全或损伤有关&#xff0c;是影响生活质量的严重健康问题。因此研究听觉器官发生过程中的关键基因对于探索听力损伤的潜在策略至关重要。斑马鱼模型在理解内耳发…

基于SSM的校园办公管理系统的设计与实现(源码完整)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据你想解决的问题&#xff0c;今天给…

噶了呀,现在的00后这么卷的吗?

现在的小年轻真的卷得过分了。前段时间我们公司来了个00年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪20K&#xff0c;都快接近我了。 后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 最近和他聊了一次天&#xff0c;原来这位小老弟家里…

企业微信也能接入ChatGPT啦~你也能成功,步骤超详细~

文章目录 配置企业微信创建企业创建应用 配置项目一、OpenAI账号注册二、克隆项目代码三、复制配置文件四、企业微信配置 服务器购买运行项目安装Python安装核心依赖启动项目 个人微信绑定 上次我把ChatGPT接入了微信&#xff08;请看这篇文章当ChatGpt接入微信群之后&#xff…

前几天面了个30岁左右的测试员,年薪50w问题基本都能回答上,必是刷了不少八股文···

互联网行业竞争是一年比一年严峻&#xff0c;作为测试工程师的我们唯有不停地学习&#xff0c;不断的提升自己才能保证自己的核心竞争力从而拿到更好的薪水&#xff0c;进入心仪的企业&#xff08;阿里、字节、美团、腾讯等大厂.....&#xff09; 所以&#xff0c;大家就迎来了…

论文笔记: Trajectory Clustering: A Partition-and-Group Framework

07 Sigmoid 使用类DBSCAN的思路对轨迹聚类 1 intro 1.1 轨迹聚类 现有的轨迹聚类算法是将相似的轨迹作为一个整体进行聚类&#xff0c;从而发现共同的轨迹。 但是这样容易错过一些共同的子轨迹&#xff08;sub-trajectories&#xff09;。而在实际中&#xff0c;当我们对特…