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、内存、带宽)受到物理服务…...

【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能
指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...

高效的后台管理系统——可进行二次开发
随着互联网技术的迅猛发展,企业的数字化管理变得愈加重要。后台管理系统作为数据存储与业务管理的核心,成为了现代企业不可或缺的一部分。今天我们要介绍的是一款名为 若依后台管理框架 的系统,它不仅支持跨平台应用,还能提供丰富…...

aurora与pcie的数据高速传输
设备:zynq7100; 开发环境:window; vivado版本:2021.1; 引言 之前在前面两章已经介绍了aurora读写DDR,xdma读写ddr实验。这次我们做一个大工程,pc通过pcie传输给fpga,fpga再通过aur…...