有干货、更有故事,微信搜索【编程指北】关注这个不一样的程序员,等你来撩~

注:这篇文章好好看完一定会让你掌握好指针的本质

C语言最核心的知识就是指针,所以,这一篇的文章主题是「指针与内存模型」

说到指针,就不可能脱离开内存,学会指针的人分为两种,一种是不了解内存模型,另外一种则是了解。

不了解的对指针的理解就停留在“指针就是变量的地址”这句话,会比较害怕使用指针,特别是各种高级操作。

而了解内存模型的则可以把指针用得炉火纯青,各种 byte 随意操作,让人直呼 666。

一、内存本质

编程的本质其实就是操控数据,数据存放在内存中。

因此,如果能更好地理解内存的模型,以及 C 如何管理内存,就能对程序的工作原理洞若观火,从而使编程能力更上一层楼。

大家真的别认为这是空话,我大一整年都不敢用 C 写上千行的程序也很抗拒写 C。

因为一旦上千行,经常出现各种莫名其妙的内存错误,一不小心就发生了 coredump… 而且还无从排查,分析不出原因。

相比之下,那时候最喜欢 Java,在 Java 里随便怎么写都不会发生类似的异常,顶多偶尔来个 NullPointerException,也是比较好排查的。

直到后来对内存和指针有了更加深刻的认识,才慢慢会用 C 写上千行的项目,也很少会再有内存问题了。(过于自信

「指针存储的是变量的内存地址」这句话应该任何讲 C 语言的书都会提到吧。

所以,要想彻底理解指针,首先要理解 C 语言中变量的存储本质,也就是内存。

1.1 内存编址

计算机的内存是一块用于存储数据的空间,由一系列连续的存储单元组成,就像下面这样,

每一个单元格都表示 1 个 Bit,一个 bit 在 EE 专业的同学看来就是高低电位,而在 CS 同学看来就是 0、1 两种状态。

由于 1 个 bit 只能表示两个状态,所以大佬们规定 8个 bit 为一组,命名为 byte。

并且将 byte 作为内存寻址的最小单元,也就是给每个 byte 一个编号,这个编号就叫内存的地址

这就相当于,我们给小区里的每个单元、每个住户都分配一个门牌号: 301、302、403、404、501…

在生活中,我们需要保证门牌号唯一,这样就能通过门牌号很精准的定位到一家人。

同样,在计算机中,我们也要保证给每一个 byte 的编号都是唯一的,这样才能够保证每个编号都能访问到唯一确定的 byte。

1.2 内存地址空间

上面我们说给内存中每个 byte 唯一的编号,那么这个编号的范围就决定了计算机可寻址内存的范围。

所有编号连起来就叫做内存的地址空间,这和大家平时常说的电脑是 32 位还是 64 位有关。

早期 Intel 8086、8088 的 CPU 就是只支持 16 位地址空间,寄存器地址总线都是 16 位,这意味着最多对 2^16 = 64 Kb的内存编号寻址。

这点内存空间显然不够用,后来,80286 在 8086 的基础上将地址总线地址寄存器扩展到了20 位,也被叫做 A20 地址总线。

当时在写 mini os 的时候,还需要通过 BIOS 中断去启动 A20 地址总线的开关。

但是,现在的计算机一般都是 32 位起步了,32 位意味着可寻址的内存范围是 2^32 byte = 4GB

所以,如果你的电脑是 32 位的,那么你装超过 4G 的内存条也是无法充分利用起来的。

好了,这就是内存和内存编址。

1.3 变量的本质

有了内存,接下来我们需要考虑,int、double 这些变量是如何存储在 0、1 单元格的。

在 C 语言中我们会这样定义变量:

int a = 999;
char c = 'c';
  • 1
  • 2

当你写下一个变量定义的时候,实际上是向内存申请了一块空间来存放你的变量。

我们都知道 int 类型占 4 个字节,并且在计算机中数字都是用补码(不了解补码的记得去百度)表示的。

999换算成补码就是:0000 0011 1110 0111

这里有 4 个byte,所以需要四个单元格来存储:

有没有注意到,我们把高位的字节放在了低地址的地方。

那能不能反过来呢?

当然,这就引出了大端和小端。

像上面这种将高位字节放在内存低地址的方式叫做大端

反之,将低位字节放在内存低地址的方式就叫做小端

上面只说明了 int 型的变量如何存储在内存,而 float、char 等类型实际上也是一样的,都需要先转换为补码。

对于多字节的变量类型,还需要按照大端或者小端的格式,依次将字节写入到内存单元。

记住上面这两张图,这就是编程语言中所有变量的在内存中的样子,不管是 int、char、指针、数组、结构体、对象… 都是这样放在内存的。

二、指针是什么东西?

2.1 变量放在哪?

上面我说,定义一个变量实际就是向计算机申请了一块内存来存放。

那如果我们要想知道变量到底放在哪了呢?

可以通过运算符&来取得变量实际的地址,这个值就是变量所占内存块的起始地址。

(PS: 实际上这个地址是虚拟地址,并不是真正物理内存上的地址

我们可以把这个地址打印出来:

printf("%x", &a);
  • 1

大概会是像这样的一串数字:0x7ffcad3b8f3c

2.2 指针本质

上面说,我们可以通过&符号获取变量的内存地址,那获取之后如何来表示这是一个地址,而不是一个普通的值呢?

也就是在 C 语言中如何表示地址这个概念呢?

对,就是指针,你可以这样:

int *pa = &a;
  • 1

pa 中存储的就是变量 a的地址,也叫做指向 a的指针。

在这里我想谈几个看起来有点无聊的话题:

为什么我们需要指针?直接用变量名不行吗?

当然可以,但是变量名是有局限的。

变量名的本质是什么?

是变量地址的符号化,变量是为了让我们编程时更加方便,对人友好,可计算机可不认识什么变量 a,它只知道地址和指令。

所以当你去查看 C 语言编译后的汇编代码,就会发现变量名消失了,取而代之的是一串串抽象的地址。

你可以认为,编译器会自动维护一个映射,将我们程序中的变量名转换为变量所对应的地址,然后再对这个地址去进行读写。

也就是有这样一个映射表存在,将变量名自动转化为地址:

a  | 0x7ffcad3b8f3c
c  | 0x7ffcad3b8f2c
h  | 0x7ffcad3b8f4c
....

http://www.taodudu.cc/news/show-4541884.html

相关文章:

  • 快速入门Docker
  • 你的眼中满是“变量”,可“变量”眼中是无相(Python)(Java与Python学习通法)
  • Kafka 实战 (1):消息中间件原理与概念
  • 2021Java面经:java封装的概念
  • kafka的基本概念和工作流程分析
  • 盒子科技笔试Java_丰巢科技 Java高级笔试面试题 PDF 下载
  • 丰巢快递柜收费,究竟挑动了我们哪根神经?
  • 2019年丰巢科技Java面试题
  • 丰巢科技面试题(2019年JAVA)
  • 丰巢科技-Java高级
  • 丰巢面试经验
  • 哈利波特(分院帽)
  • ChartDirector 6.3(C ++版)教程分享——图标饼图
  • Jquery实现京东tab切图
  • 国内网络安全公司、社区简介
  • arduino 驱动_Arduino驱动的My Little Pony捐款箱
  • 刘群:基于深度学习的自然语言处理,边界在哪里?
  • 用C++编写出《哈利波特》的分院帽程序,不要错过哦~
  • ai人工智能_对人工智能的追求
  • 一个续写故事达到人类水平的AI,OpenAI大规模无监督语言模型GPT-2...
  • Task 4: Contextual Word Embeddings
  • 2021年亚太地区15个最佳职场榜单;人均养老消费预期将超百万元;高净值人群财富传承方式依然首选保险 | 美通社头条...
  • 买一台 iPhone X,还是创建一家未来的独角兽?
  • 买一台 iPhone X,还是创建一家未来的独角兽?
  • [预训练语言模型专题] 银色独角兽GPT家族
  • VUE头像轮播组件
  • 达摩院用128张GPU烧出“中文版GPT-3”,我试了下,这文风不是开往幼儿园的车…...
  • 九天·毕昇 试玩体验
  • 详解变分自编码器VAE(Variational Auto-Encoder)
  • 简单认识一点C语言

C语言重点——指针篇(一文让你完全搞懂指针)| 从内存理解指针 | 指针完全解析相关推荐

  1. lambda表达式java项目常用_一文带你彻底搞懂Lambda表达式

    1. 为什么使用Lambda表达式 Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递).可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风 ...

  2. 一文带你彻底搞懂i++和++i的区别,谁的效率更高?

    作者简介:Codebowl靓仔,学妹的工具人,C++开发误入数据开发,梦想30岁退休的靓仔就是我啦. i++和++i对于初学者来说,一直是一个特别容易搞混的内容,相信很多人现在也没有完全搞清(作者初学 ...

  3. 一文让你彻底搞懂AQS(通俗易懂的AQS)

    一文让你彻底搞懂AQS(通俗易懂的AQS) 一.什么是AQS AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Se ...

  4. 一文带你彻底搞懂C++中一些常见指针(形如*p)的用法

    提高指针效率以及程序可读性 为什么要后置运算符? 有C语言编程经验的人可能会感到疑问,为什么在C++里面我们推荐优先使用运算符的前置版本,就是把运算符号放在变量的前面,前置版本的递增避免了不必要的工作 ...

  5. java中对递归的限制是什么_什么是递归,通过这篇文章,让你彻底搞懂递归

    美丽开始于你决定做自己的那一刻. 啥叫递归 tips:文章有点长,可以慢慢看,如果来不及看,也可以先收藏以后有时间在看. 聊递归之前先看一下什么叫递归. 递归,就是在运行的过程中调用自己. 构成递归需 ...

  6. 开发人员必学!这篇入门你必须了解!搞懂这些直接来阿里入职

    前言 Netty 是一款基于 Java 的网络编程框架,能为应用程序管理复杂的网络编程.多线程处理以及并发.Netty 隐藏了样板和底层代码,让业务逻辑保持分离,更加易于复用.使用 Netty 可以得 ...

  7. 一文带你轻松搞懂事务隔离级别(图文详解)

    本文由 SnailClimb 和读者 BugSpeak 共同完成. 事务隔离级别(图文详解) 什么是事务? 事务是逻辑上的一组操作,要么都执行,要么都不执行. 事务最经典也经常被拿出来说例子就是转账了 ...

  8. 一文带你你搞懂索引如何优化!!!

    前言 索引的相信大家都听说过,但是真正会用的又有几人?平时工作中写SQL真的会考虑到这条SQL如何能够用上索引,如何能够提升执行效率? 此篇文章详细的讲述了索引优化的几个原则,只要在工作中能够随时应用 ...

  9. 一文让你彻底搞懂浏览器的渲染流程

    hello,大家好.上次为大家介绍了重排和重绘的一些内容,其中涉及到的浏览器渲染流程自己就没有在上篇博文详细介绍了.今天我们就来好好唠唠浏览器的整个渲染流程. 首先,我们知道,一个页面通常由三个部分组 ...

最新文章

  1. h5仿微信聊天(高仿版)、微信聊天表情|对话框|编辑器
  2. 医院计算机网络安全宣教,医院网络安全及解决方案
  3. JS 原型链 prototypt 和隐式原型 _proto_
  4. notepad++ 远程连接阿里云服务器
  5. foreach循环符合就不往下走了_柴油发电机组冷却液循环故障解决方法
  6. 《Python Cookbook 3rd》笔记(3.10):矩阵与线性代数运算
  7. 使用case语句的3个诀窍
  8. Java对象的serialVersionUID在序列化和反序列化的用途
  9. wget整站抓取、网站抓取功能
  10. Flink catalog简单使用
  11. EasyRecovery14永久免费版密钥电脑硬盘恢复教程
  12. 计算年龄:DATEDIF函数
  13. Mac python 安装信息安全,Pycrypto 出现,C compiler cannot create executablesC编辑器不能创建可执行文件
  14. 如何规避适配风险?以《乱世王者》为例,探秘手游兼容性测试之路
  15. [RTOS]rtthread,freeRTOS,uCOS等系统简单对比
  16. App测试分类总结及方法
  17. 开始闭关修炼 冥思微软之大未来
  18. Docker 的使用和部署(Daocloud)
  19. 视频教程-FPS游戏逆向与安全+UE4引擎基础详解-其他
  20. 【自学笔记】天地图添加标注

热门文章

  1. ery validator addMethod 方法的使用
  2. ajax的leiku,AJAX实现汉字和拼音搜索自动提示的效果(asp.net)
  3. 02_C++实现多线程服务器代码(linux系统)
  4. Python.习题五 列表与元组(下)
  5. PI API 基础函数(一)
  6. 实现输入一行字符,分别统计出其中英文字母、空格、数字和其他字符的个数。
  7. 如何用手机上的计算机弹音乐,如何使用手机qq音乐遥控电脑播歌
  8. centos切换jdk版本
  9. Android实现在线播放音乐
  10. app自动化测试——app自动化控制、常见控件定位方法