C语言的灵魂——指针(1)
指针是C语言的灵魂,有了指针C语言才能完成一些复杂的程序;没了指针就相当于C语言最精髓的部分被去掉了,可见指针是多么重要。废话不多讲我们直接开始。
指针
- 一,内存和地址
- 二,编址
- 三,指针变量和地址
- 1,&(取地址)操作符
- 2,指针变量
- 3,解引用操作符
- 4,指针变量的大小
- 四,指针类型的意义
- 五,偏移量
- 六,指针的运算
一,内存和地址
谈到指针我们首先就会想到地址,因为指针就是地址。为什么这么会认为指针它就是地址呢?指针与地址有什么关联呢?想要回答上面两个问题那我们就先来探究一下内存和地址的关系。
我们举一个生活中的例子你就知道了,我们生活中所看见的学校的宿舍楼其实就是内存,比如一栋一栋的宿舍喽在建造之初就规定好了有多少个房间,房间里能住几个人。这可以说是宿舍楼的内存;而计算机也一样,计算机就是将一个大内存分成若干个小的内存单元,每个内存单元占一个字节,这里就相当于一栋宿舍楼有若干间数;而每一字节占8个比特位,就相当于一间能住8个人。 这里给出一些常见的内存单位

了解完内存我们来说说地址,地址就相当于门牌号,平时在购物时买东西填的地址填的可不就是家里的门牌号嘛,所以通过门牌号(地址)快递员才能找对地方给我们派件;对应计算机也是一样的,地址为每一个内存进行编号。比如上面所说的宿舍楼每一间都有不同的门牌号,只不过C语言给地址取了一个新的名字叫指针我们给出一张图来让你更直观和清晰的了解内存和地址的关系。

了解完了内存和地址接下来我们来了解一下编址。
二,编址
谈到编址就不得不谈到一些硬件上的知识了,要了解编址首先必须理解,计算机内是有很多的硬件单元,而硬件单元是要互相协同工作的。所谓的协同,至少相互之间要能够进行数据传递。但是硬件与硬件之间是互相独立的,那么如何通信呢?
答案很简单,用"线"连起来。 而CPU和内存之间也是有大量的数据交互的,所以,两者必须也用线连起来。 不过,我们今天关心一组线,叫做地址总线。
我们画图来让大家更好的理解:

搞清楚上图的原理我们来说说为什么要编址?
CPU访问内存中的某个字节空间,必须知道这个 字节空间在内存的什么位置,而因为内存中字节很多,所以需要给内存进⾏编址(就如同宿舍很多,需要给宿舍编号⼀样)。但是计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的。
上面说到什么地址总线,数据总线,控制总线那大家是不是很好奇,为什么电路板上会有这么多的线呢?
在硬件电路中只有高电平和低电平这两种状态,这两种状态分别代表0和1,但是你想想既然一根能表示两种状态,那么2根能表示3种,以此类推以2的n次方呈现指数式增长,而线越多能表示的东西也越多,比如我们现在用的64位机器就可以表示2^64这么多种含义而每一种含义都代表一个地址;地址信息被下达给内存,在内存上,就可以找到 该地址对应的数据,将数据在通过数据总线传入CPU内寄存器。
三,指针变量和地址
1,&(取地址)操作符
既然指针与地址关系那么密切那我们就有必要来学习一下&(取地址)操作符,我们给出一段代码:
#include<stdio.h>
int main()
{int a = 10;printf("%p",&a);//%p是专门用来打印地址的占位符 注意是&a
}

我们也可以调试起来让大家来看:

但有一点需要特别注意取地址取出来的是较小的地址,因为我们知道数据类型占几个字节又知道较小的地址剩下的顺藤摸瓜直接往后推算即可。
我们来看看在内存中是怎么样存储的:

2,指针变量
我们已经了解了&操作符,并且已经拿到了变量的地址,那么这时就会有人问了:我们已经拿到了变量的地址,怎么去存放它呢?答案是使用指针变量。
#include<stdio.h>
int main()
{int a = 10;char b = 'b';char * ch = &b;int * p = &a;
}
上面我们定义了两个指针变量分别为ch和p来储存a和b的地址,这样我们就可以通过访问指针变量来间接访问a和b。我们如何来看待指针变量呢?
这里p左边写的是 int , * 是在说明p是指针变量,而前面的 int 是在说明pa指向的是整型(int) 类型的对象。
ch左边写的是 char , * 是在说明ch是指针变量,而前面的 char 是在说明pa指向的是整型(int) 类型的对象。
画一个图你就明白了:

当我们了解了如何存储地址后肯定会想这如何去使用它,这就引出了我们的解引用操作符。
3,解引用操作符
在现实生活中,我们使用地址要找到⼀个房间,在房间里可以拿去或者存放物品。 C语言中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象,这里必须学习⼀个操作符叫解引用操作符(*)。
举个例子:
#include<stdio.h>
itn main()
{int a = 100;printf("a=%d\n",a);int * p = &a;*p=0;//*p拿到了p所指向的对象 a 对*p操作即是对a操作 这里就是间接访问printf("a=%d",*p);//这里注意打印的一定是*p而不是preturn 0;
}

从运行结果来看我们看到,通过 * 这个解引用操作可以间接来改变a的值。 * p实际上就是通过通过pa中存放的地址,找到指向的空间所以 * p本质上就是a变量了。

另外值得注意的是一定要分清 * p和p 前者是对p这个指针变量进行解引用操作;后者则是指针变量其存储的是p所指向对象(a)的地址。
说完了解引用我们来看看指针变量的大小。
4,指针变量的大小
32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4 个字节才能存储。
如果指针变量是⽤来存放地址的,那么指针变量的大小就得是4个字节的空间才可以。
同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要 8个字节的空间,指针变量的大小就是8个字节。
#include <stdio.h>
//指针变量的⼤⼩取决于地址的⼤⼩
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{ //zd是专门用来打印sizeof的返回值的printf("%zd\n", sizeof(char *)); printf("%zd\n", sizeof(short *)); printf("%zd\n", sizeof(int *)); printf("%zd\n", sizeof(double *)); return 0;
}
我们分别让它在32位和64位平台下运行看看会有什么结果:


我么可以看到在不同平台下的结果是不同的所以我们可以知道:
• 32位平台下地址是32个bit位,指针变量大小是4个字节
• 64位平台下地址是64个bit位,指针变量大小是8个字节
• 注意指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。

既然指针变量的大小和类型无关,只要是指针变量,在同⼀个平台下,大小都是⼀样的,为什么还要有各种各样的指针类型呢?接下来我们就来探究一下指针类型的意义。
四,指针类型的意义
我们先来看两段代码:
#include <stdio.h>
int main()
{ int n = 0x11223344; int *pi = &n; *pi = 0; return 0;
}
我们来看调试结果:

#include <stdio.h>
int main()
{int n = 0x11223344; char *pc = (char *)&n; //强制转换为char*的地址 赋给字符指针变量pc*pc = 0; return 0;
}
我们直接看调试结果:

对比这两次调试结果不知各位读者有没有发现第一次*pi变成0结果整个n的值(4个字节)都变为了0。
而第二次*pc变成零只有前面一个字节变成了0。
由此我们得出结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作指针所指对象的几个字节)。 比如: char* 的指针解引⽤就只能访问⼀个字节,而int* 的指针的解引⽤就能访问四个字节。 操作的一定是指针所指对象的空间!
五,偏移量
偏移量相信大家在物理中都能经常听到,而偏移量在指针中代码什么意思呢?偏移量指的就是指针变量加减整数时的那个数字叫做偏移量。偏移量是为了形象说明不是专有名词哈。
举个例子:
#include<stdio.h>
int main()
{int a=0;char*pc=(char*)&a;int *pi=&a;printf("&a = %p\n",&a);printf("pc = %p\n",pc);printf("pc+1 = %p\n",pc+1);printf("pi = %p\n",pi);printf("pi+1 = %p\n",pi+1);return 0;
}

从上图我们可以看到:
我们可以看出, char 类型的指针变量+1跳过1个字节, int 类型的指针变量+1跳过了4个字节。 这就是指针变量的类型差异带来的变化。指针+1,其实跳过1个指针指向的元素。指针可以+1,那也可 以-1。偏移的其实就是一个类型的大小比如int类型 +1就偏移4个字节。
结论:指针的类型决定了指针向前或者向后偏移的距离。
了解完了这些基本的知识后我们就来看一些有关指针的的运算
六,指针的运算
指针的基本运算有三种,分别是:
1,指针± 整数
2,指针-指针
3,指针的关系运算
我们依次给出代码大家可以体会一下:
1,指针加整数型
#include<stdio.h>
int main()
{//指针加整数型int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr;for (int i = 0;i < sz;i++){//printf("%d \n", p);//注意这样打印出来的是以%d形式打印的地址//printf("%p \n", p);//这样是打印所有元素的地址printf("%d ", *p);//加上*号是解引用 这样打印的才是所有元素p++;}return 0;
}
#include<stdio.h>
int main()
{ int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr;for (int i = 0;i < sz;i++){printf("%d ", *(p + i));//(p+i)括号指针变量加整数是偏移量的意思即 往后取i个地址并把该地址的内容以%d形式打印出来}return 0;
}
2,指针减指针,及指针的关系运算
#include <stdio.h>
int my_strlen(char *s)
{ char *p = s; while(*p != '\0' ) p++; return p-s;
}
int main()
{ printf("%d\n", my_strlen("abc")); return 0;
}
以上就是本章的全部内容啦!
感谢能够看到这里的读者,如果我的文章能够帮到你那我甚是荣幸,文章有任何问题都欢迎指出!制作不易还望给一个免费的三连,你们的支持就是我最大的动力!

相关文章:
C语言的灵魂——指针(1)
指针是C语言的灵魂,有了指针C语言才能完成一些复杂的程序;没了指针就相当于C语言最精髓的部分被去掉了,可见指针是多么重要。废话不多讲我们直接开始。 指针 一,内存和地址二,编址三,指针变量和地址1&#…...
vue2和vue3指令
Vue 2 和 Vue 3 的指令系统非常相似,但 Vue 3 在指令方面进行了优化和扩展。以下是 Vue 2 和 Vue 3 中指令的对比: 1. 通用指令 这些指令在 Vue 2 和 Vue 3 中都可以使用,功能一致: 指令说明v-bind绑定 HTML 属性或组件 propsv-…...
【超详细】ELK实现日志采集(日志文件、springboot服务项目)进行实时日志采集上报
本文章介绍,Logstash进行自动采集服务器日志文件,并手把手教你如何在springboot项目中配置logstash进行日志自动上报与日志自定义格式输出给logstash。kibana如何进行配置索引模式,可以在kibana中看到采集到的日志 日志流程 logfile-> l…...
微信阅读网站小程序的设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
通过配置核查,CentOS操作系统当前无多余的、过期的账户;但CentOS操作系统存在共享账户r***t
通过配置核查,CentOS操作系统当前无多余的、过期的账户;但CentOS操作系统存在共享 核查CentOS操作系统中的用户账户,可以使用以下命令: 查看当前活跃用户: awk -F: /\$1\$/{print $1} /etc/shadow 查看多余账户(非活跃账户&…...
Vue 3 30天精进之旅:Day 05 - 事件处理
引言 在前几天的学习中,我们探讨了Vue实例、计算属性和侦听器。这些概念为我们搭建了Vue应用的基础。今天,我们将专注于事件处理,这是交互式Web应用的核心部分。通过学习如何在Vue中处理事件,你将能够更好地与用户进行交互&#…...
.NET Core跨域
CORS 跨域通讯的问题。解决方案:JSONP、前端代理后端请求、CORS等。CORS原理:在服务器的响应报文头中通过access-control-allow-origin告诉浏览器允许跨域访问的域名。在Program.cs的“var appbuilder.Build()”这句代码之前注册 string[] urls new[] …...
笔试-二维数组2
应用 现有M(1<M<10)个端口组,每个端口组是长度为N(1<N<100),元素均为整数。如果这些端口组间存在2个及以上的元素相同,则认为端口组可以关联合并;若可以关联合并,请用二位数组表示输出结果。其中…...
vue中使用jquery 实现table 拖动改变尺寸
使用 CDN , 降低打包文件的大小在index.html中 <script src"https://.../cdns/jquery-1.12.4.min.js"></script>在 Vue 中使用 jQuery 一旦你引入 jQuery,你可以在 Vue 实例中使用它。有两种主要方式: 1. 使用全局变量 $ jQue…...
使用ensp进行ppp协议综合实验
实验拓扑 实验划分 AR1的Serial3/0/0接口:192.168.1.1/24; AR2的Serial3/0/0接口:192.168.1.2/24; AR2的Serial3/0/1和4/0/0的聚合接口:192.168.2.2/24; AR3的Serial3/0/0和3/0/1的聚合接口:192…...
什么是AGI
AGI(Artificial General Intelligence,人工通用智能)是指具备与人类相当或超越人类水平的通用智能的人工智能系统。与当前主流的**狭义人工智能(Narrow AI)**不同,AGI 能够像人类一样灵活地处理各种任务&am…...
RabbitMQ模块新增消息转换器
文章目录 1.目录结构2.代码1.pom.xml 排除logging2.RabbitMQConfig.java3.RabbitMQAutoConfiguration.java 1.目录结构 2.代码 1.pom.xml 排除logging <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/PO…...
验证二叉搜索树(力扣98)
根据二叉搜索树的特性,我们使用中序遍历,保证节点按从小到大的顺序遍历。既然要验证,就是看在中序遍历的条件下,各个节点的大小关系是否符合二叉搜索树的特性。双指针法和适合解决这个问题,一个指针指向当前节点&#…...
vue3 vue2区别
Vue 3 和 Vue 2 之间存在多个方面的区别,以下是一些主要的差异点: 1. 性能改进 Vue 3:在性能上有显著提升,包括更小的包体积、更快的渲染速度和更好的内存管理。Vue 2:性能相对较低,尤其是在大型应用中。…...
IOS 自定义代理协议Delegate
QuestionViewCell.h文件代码,定义代理协议 protocol QuestionViewCellDelegate <NSObject>- (void)cellIsOpenDidChangeAtIndexPath:(NSIndexPath *)indexPath;endinterface QuestionViewCell : UITableViewCellproperty (nonatomic, weak) id<QuestionVi…...
消息队列篇--扩展篇--码表及编码解码(理解字符字节和二进制,了解ASCII和Unicode,了解UTF-8和UTF-16,了解字符和二进制等具体转化过程等)
1、理解字符,int,字节以及二进制存储 (1)、字符 字符是文本的基本单位,例如字母(A, B, C)、数字(1, 2, 3)、标点符号(!, ?, ,)以及其他符号&am…...
2024年度总结——理想的风,吹进现实
2024年悄然过去,留下了太多美好的回忆,不得不感慨一声时间过得真快啊!旧年风雪尽,新岁星河明。写下这篇博客,记录我独一无二的2024年。这一年,理想的风终于吹进现实! 如果用一句话总结这一年&am…...
代码工艺:实践 Spring Boot TDD 测试驱动开发
TDD 的核心理念是 “先写测试,再写功能”,其过程遵循一个严格的循环,即 Red-Green-Refactor: TDD 的流程 1. Red(编写失败的测试) 根据需求,先编写一个测试用例,描述期望的行为。…...
深度学习|表示学习|卷积神经网络|通道 channel 是什么?|05
如是我闻: 在卷积神经网络(CNN)中,channel(通道) 是指输入或输出数据的深度维度,通常用来表示输入或输出的特征类型。 通道的含义 输入通道(Input Channels):…...
PCDN的虚拟机与云主机区别
使用虚拟机和云主机运行PCDN(P2P CDN)时,主要存在以下区别: 一、资源分配与灵活性 虚拟机: 资源受限:虚拟机运行在物理服务器上,其资源(如CPU、内存、带宽)受到物理服务…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
