【C语言】—— 指针一 : 初识指针(上)

【C语言】—— 指针一 : 初识指针(上)

    • 一、内存和地址
      • 1.1、如何理解内存和地址
      • 1.2、 如何理解编址
    • 二、指针变量和地址
    • 三、指针变量类型的意义
    • 四、指针运算
      • 5.1、指针 +- 整数
      • 5.2、指针 -- 指针
      • 5.3、指针关系的运算

一、内存和地址

1.1、如何理解内存和地址

  
  在讲内存和地址之前,我想给大家讲一个生活中的案例。
  
  假设我们要找好朋友,他只告诉你在哪栋楼,但那栋楼没有门牌号。这时,你想找到他,只能挨家挨户地去寻找,这样效率无疑会很低,而且其他住户也会有意见。
  
  因此为了方便查找与管理,楼栋都会给每个房间上一个门牌号,有了门牌号,就能快速定位朋友所在的房间。
  
  在生活中,我们给每个房间一个房间号,提高效率,那如果对照计算器中又会是怎样的呢?
  
  CPU(中央处理器)在处理数据的时候,需要的数据是从内存中读取的,处理后的数据也会放在内存中。我们知道,买电脑时,电脑内存有 8GB / 16GB / 32GB,那么电脑是如何管理这些内存的呢?
  
  其实计算机也是把内存分为一个个内存单元,每个内存单元大小取一个字节。每个内存单元有一个编号 (这个编号相当于房间的门牌号),有了这个内存单元的编号,CPU就可以快速访问一个内存空间。
  
  生活中,我们把门牌号叫做地址,在计算机中,我们把内存单元的编号也称为地址
  
  C语言中给地址起了个新名字:指针
  
  我们可以这样理解:

内存单元的编号 ==  地址 == 指针

1.2、 如何理解编址

  
  CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,⽽因为内存中字节很多,所以需要给内存进⾏编址
  
  计算机中的编址,并不是把计算机每个字节的地址记录下来,而是通过硬件设计来完成。
  
  首先,我们要知道计算机内有许多硬件单元,硬件单元的相互协作,需要不断进行数据传递
  
  那么问题来了,硬件与硬件之间是相互独立的,那么他们是如何交流信息的呢?答案其实并不神秘:那就是用线相连。
  
  CPU与内存之间也有大量的数据交互,他两也必须用线连起来。
  
  今天,我们关心的一组线,叫地址总线
  
  我们可以简单理解,32位机器有32根地址总线,每根线只有两种状态:01 【电脉冲的有无】。所以,一根线只有两种状态,只能表示两种含义,而 32 根线就能表示 2 32 2^{32} 232 中含义,每一种含义都代表着一个地址
  
  地址信息被下达给内存,在内存上,就可以找到该地址所对应的数据,而数据通过数据总线传入CPU内寄存器。

在这里插入图片描述

  
  

二、指针变量和地址

2.1、取地址操作符 &

  
  在C语言中,创建一个变量本质上就是向内存申请空间
  
例如:

在这里插入图片描述

  
  上述代码就是为 a a a 创建了一个 4 个字节大小的内存空间,里面存放的是10(16进制表示)
  

四个地址分别是:

  • 0x00AFF830
  • 0x00AFF831
  • 0x00AFF832
  • 0x00AFF833

  
  而我们想要得到 a a a 的地址,就需要用到取地址操作符(&)
  
  学习前面的知识,我们知道,每个字节都有一个地址,而整型变量 a a a 一共申请了 4 个字节,那&操作符取出的地址是哪一个呢?
  

#include<stdio.h>

int main()
{
	int a = 10;//为a申请4个字节的空间
	
	&a;//取出a的地址

	printf("%p\n", &a);

	return 0;
}

  
  由于编译器每一次运行给变量分配的地址都不同,这里我就不打印了
  
  & a a a 取出的是 a a a 所占 4 个字节中最小的地址
  
  按上面的例子,即:0x00AFF830
  
  

2.2、 指针变量

  
  我们用取地址操作符取出变量的地址,把他放在哪呢?不存储起来以后想要使用怎么办呢?地址其实也只是一个数值而已,如地址:0x0012FF40,总不能让他无家可归吧。
  
  就像上述例子中的 10,创建了整型变量 a a a 来存放,C语言中也提供了专门的变量类型来存放指针,那就是:指针变量
  

#include<stdio.h>

int main()
{
	int a = 10;//为a申请4个字节的空间
	int* pa = &a;//取出a的地址放在指针变量p中
	printf("%p\n", pa);
	return 0;
}

  
  上述代码中, p a pa pa 就是一个指针变量,他的类型是 int*
  

我们该如何理解int*呢?

  • ∗ * 表示他是指针变量
  • i n t int int 表示他所存放的地址所指向的类型为 i n t int int 类型

  

在这里插入图片描述

  
  而同理,指针变量还有其他类型:

  • l o n g ∗ long* long   指向 l o n g long long 类型
  • c h a r ∗ char* char   指向 c h a r char char 类型
  • s h o r t ∗ short* short  指向 s h o r t short short 类型
  • … … … …

  

2.3、 解引用操作符 ∗ *

  
  我们用取地址操作符(&)把变量的地址取出来有什么用呢?那自然是为了方便未来要找到他。
  
  我们只要拿到了地址(指针),就能通过地址(指针)找到他所指向的对象,而这个过程就需要用到解引用操作符(*)
  

#include<stdio.h>

int mian()
{
	int a = 0;
	int* pa = &a;
	*pa = 100;
	return 0;
}

  

  上述代码中*pa = 100;就运用了解引用操作符,* pa 的意思是:通过pa存放的地址,找到其指向的空间,那么*pa其实就是变量 a 了;使用*pa = 100;就是把 a a a 变成了 100.

  

2.4、指针变量的大小

  
  前面的学习中,我们了解到,32位的机器设有32根地址总线,每根线只有两种状态:即有电流通过和无电流通过,逻辑上表示 01 ,这与二进制序列的表示相符。我们把32位的地址总线产生的二进制序列看成一个地址,那么这个地址一共有32个 b i t bit bit,即 4 个字节才能存储。
  
  同理,64位的机器,存储地址需要 8 个字节的空间。
  

  • 32位平台下地址是32个 b i t bit bit 位,指针变量大小是 4 个字节
  • 64位平台下地址是64个 b i t bit bit 位,指针变量大小是 8 个字节

  
注意:指针变量的大小与类型无关,只要他是指针变量,他的大小就是 4 字节或 8 字节
  
  

三、指针变量类型的意义

3.1、 指针的解引用

  
  我们得知,指针变量的大小和类型无关,那么为什么还要有各种各样的指针类型呢?
  
  C语言当然不可能无缘无故创造这么多类型,他们都是有意义的,让我们一起往下看。
  
下面,我们来看看两段代码:

#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;
	*pc = 0;
	return 0;
}

在这里插入图片描述

  
  从调试我们可以看到,代码 1 会将 n n n 的 4 个字节全部改为 0 ,而代码 2 只是将 n n n第一个字节改为 0
  

指针的类型决定了对指针解引用时,指针有多大的访问权限(一次能操作几个字节)

例如: c h a r char char* 只能访问 1 个字节, i n t int int* 能访问 4 个字节
  
  

3.2、 指针 ± 整数

  
  让我们先来看一段代码,观察他们的地址变化

#include<stdio.h>

int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;

	printf("&n   =%p\n", &n);
	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;
}

运行结果:

这里是引用

  
  我们可以看到, c h a r char char* 类型 +1 跳过了 1 个字节 i n t int int* 类型 +1 跳过了 4 个字节
  
  这是因为 c h a r char char * 指针类型所指向的 c h a r char char 类型大小是 1 个字节 ,同理, i n t int int * 所指向的 i n t int int 类型大小是 4 个字节
  

指针的类型决定了指针向前或向后走一步有多大

  

3.3、 v o i d void void * 指针

  
  在所有指针类型中,还有一种特殊的指针: v o i d void void * 指针。我们知道 v o i d void void 用在函数的返回类型和参数中时,表示无返回值、无参数,那么 v o i d void void * 指针又表示什么呢?
  
  void * 可以理解为无具体类型的指针(或者叫泛型指针),它可以接收任意类型的地址
    
  但是它也有局限性:void *类型指针不能完成上面的解引用和指针 +- 整数的运算,因为 void *类型指针不知道访问权限(一次应操作几个字节),想完成上述运算,只能先强制类型转换为其他指针类型。

  
举例:

#include<stdio,h>

int main()
{
	int a = 10;
	int* pi = &a;
	char* pc = &a;
	return 0;
}

在这里插入图片描述
  
  我们可以看到,将一个 i n t int int 类型的变量地址赋值给一个 c h a r char char * 类型的指针变量。编译器给出了警告,因为类型不兼容,要想消除这个警告,除了强制类型转换成 c h a r char char * 类型,还可以用 v o i d void void *类型接收。
  
如下:

#include<stdio.h>

int main()
{
	int a = 10;
	void* pi = &a;
	void* pc = &a;

	*pi = 10;
	*pc = 0;
	return 0;
}

VS编译器运行结果:
在这里插入图片描述
  我们可以看到,虽然用 v o i d void void * 类型接收编译器并没有报警告,但是却无法解引用,编译器直接报错,那么 v o i d void void * 类型有什么用呢?
  
  一般 v o i d void void * 类型用在函数参数部分,用来接收未知类型数据的地址的,这样设计可以达到泛型编程的效果。使得一个函数可以处理多种类型的数据。

  

四、指针运算

  

指针的基本运算有三种,分别是:

  • 指针 ± 整数
  • 指针 - 指针
  • 指针的关系运算

  

5.1、指针 ± 整数

  
  在 C语言——详解数组 一文中,我曾经提到过,数组在内存中是连续存放的。因此,对于数组来说,我们只要找到了他的首元素地址,那他剩下元素的地址也能顺藤摸瓜找到。
  

#include<stdio.h>
//指针 +- 整数
int main()
{
	int arr[9] = { 1,2,3,4,5,6,7,8,9 };
	int* p = &arr[0];// 取出数组首元素的地址
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));// p + i  这里就是指针 + 整数
	}
	return 0;
}

  

在这里插入图片描述

  

  • 注:用指针访问数组的前提条件:数组在内存中是连续存放的
      

指针 ± 整数公式:

  • p + n p + n p+n − > -> >  跳过 n + s i z e o f ( t y p e ) n + sizeof(type) n+sizeof(type) 个字节
      
      

例如:

int a = 10;
int* p = &a;

  
p + 1 p + 1 p+1 跳过4 个字节 ( 1 ∗ s i z e o f ( t y p e )) (1 * sizeof(type)) 1sizeoftype))

  
  

5.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;
}

  这里,我们模拟实现了一个简陋的 s t r l e n strlen strlen 函数
  
  函数来求字符串长度,代码逻辑是:传入字符串首元素的地址(指针),然后找到字符串最后一个元素的地址(指针),两个指针相减,得到的是中间的元素格式,即整个字符串的长度啦。

  
但需要注意的是:

  • 指针能减指针,但指针不能+指针,你听说法日期加日期的吗?指针也是如此。
  • 可以低地址 – 高地址,只不过得到的是负数
  • 指针 – 指针的前提条件一定是:两个指针指向的是同一块空间

  
例如:

#include<stdio.h>

int main()
{
	int arr[10] = { 0 };
	char ch[5] = { 0 };
	printf("%d\n", &ch[4] - &arr[6]);
	return 0;
}

  上述代码是错误的代码,因为两个指针并没有指向同一块空间。
  
  

5.3、指针关系的运算

  
  指针关系运算即地址(指针)与地址(指针)比较大小
  

请看代码:

#include<stdio.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	while (p < arr + sz)// 指针大小比较
	{
		printf("%d ", *p);
		p++;
	}
}

  该代码中,循环条件判断中就运用了指针关系的运算,以确保对数组的访问不越界。

  
  
  


  好啦,本期关于指针就介绍到这里啦,希望本期博客能对你有所帮助,同时,如果有错误的地方请多多指正,让我们在C语言的学习路上一起进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/460572.html

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

相关文章

LeetCode每日一题——两数之和

两数之和OJ链接&#xff1a;1. 两数之和 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 思路&#xff1a; 在读懂题目后很多人觉得这种题目很简单&#xff0c;但是不管怎么写&#xff0c;在VS等其他编译器上能跑成功&#xff0c;但是在LeetCode上就是没办法通过。…

Java项目:54 springboot工资信息管理系统453

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本系统的使用角色可以被分为用户和管理员&#xff0c; 用户具有注册、查看信息、留言信息等功能&#xff0c; 管理员具有修改用户信息&#xff0c;发…

【回归预测】基于SSA-RF(麻雀搜索算法优化随机森林)的回归预测 多输入单输出【Matlab代码#66】

文章目录 【可更换其他算法&#xff0c;获取资源请见文章第6节&#xff1a;资源获取】1. 随机森林RF算法2. 麻雀搜索算法3. 实验模型4. 部分代码展示5. 仿真结果展示6. 资源获取 【可更换其他算法&#xff0c;获取资源请见文章第6节&#xff1a;资源获取】 1. 随机森林RF算法 …

微信小程序-webview分享

项目背景 最近有个讨论区项目需要补充分享功能&#xff0c;希望可以支持在微信小程序进行分享&#xff0c;讨论区是基于react的h5项目&#xff0c;在小程序中是使用we-view进行承载的 可行性 目标是在打开web-view的页面进行分享&#xff0c;那就需要涉及h5和小程序的通讯问…

重学SpringBoot3-函数式Web

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-函数式Web 函数式Web编程简介RouterFunctionRequestPredicateServerRequestServerResponse 好处示例结论 随着响应式编程范式的兴起和 Java 函数式编程能…

Golang中map数据结构字段解析

Golang里map底层数据结构具体如下图所示&#xff1a; map其实就是一个指向 hmap 的指针&#xff0c;占用了8个字节 hmap各自段存放的字段意义如下&#xff1a; 字段含义countmap中元素的个数&#xff0c;对应len (map)的值flags状态标志位&#xff0c;标记map的一些状态B桶数…

基于SpringBoot和Echarts的全国地震可视化分析实战

目录 前言 一、后台数据服务设计 1、数据库查询 2、模型层对象设计 3、业务层和控制层设计 二、Echarts前端配置 1、地图的展示 2、次数排名统计 三、最终结果展示 1、地图展示 2、图表展示 总结 前言 在之前的博客中基于SpringBoot和PotsGIS的各省地震震发可视化分…

Hive-技术补充-初识ANTLR

一、背景 要清晰的理解一条Hql是如何编译成MapReduce任务的&#xff0c;就必须要学习ANTLR。下面是ANTLR的官方网址&#xff0c;下面让我们一起来跟着官网学习吧&#xff0c;在学习的过程中我参考了《antlr4权威指南》&#xff0c;你也可以读下这本书&#xff0c;一定会对你有…

Java微服务轻松部署服务器

我们在日常开发微服务之后需要再服务器上面部署&#xff0c;那么如何进行部署呢&#xff0c;先把微服务的各个服务和中间件以及对应的端口列举出来&#xff0c;都打包成镜像&#xff0c;以及前端代码部署的nginx&#xff0c;使用docker-compose启动&#xff0c;访问服务器nginx…

linux板子vscode gdb 远程调试

板子&#xff1a;hi3556v200 交叉编译工具&#xff1a;arm-himix200-linux 主机&#xff1a;win10虚拟机的ubuntu16.4 gdb:gdb-8.2.tar.gz 1.在ubuntu交叉编译gdb&#xff08;Remote g packet reply is too long解决&#xff09; 建议修改gdb8.2/gdb目录下面的remote.c解决…

16.WEB渗透测试--Kali Linux(四)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;15.WEB渗透测试--Kali Linux&#xff08;三&#xff09;-CSDN博客 1.crunch简介与使用 C…

docker实战(3)

1, jdk17版本的dockerfile FROM eclipse-temurin:17-jre ADD ./target/ods-poc-0.0.1-SNAPSHOT.jar /home/app/app.jar WORKDIR /home/app ENV TZ Asia/Shanghai ENTRYPOINT ["java","-jar","/home/app/app.jar"] EXPOSE 8083dockerfile 1, …

神策分析 Copilot 成功通过网信办算法备案,数据分析 AI 化全面落地

近日&#xff0c;神策数据严格遵循《互联网信息服务深度合成管理规定》&#xff0c;已完成智能数据问答算法备案。该算法基于大模型技术&#xff0c;专注于为客户提供数据指标查询和数据洞察方面的专业回答。 神策分析 Copilot 运用神策数据智能数据问答算法&#xff0c;聚焦分…

uni app 打肉肉(打飞机)小游戏

都给老婆和孩子写了 合十 钓鱼了&#xff0c;给自己写个打飞机吧。没找飞机怪兽的图片。就用馒头和肉肉代替了。有问题不要私信我。自己改哈 <template><view class"page_main"><view class"contentone"><canvas class"canvas…

【Session】Tomcat Session 集群

设备 nginx&#xff1a;192.168.67.11 tomcat1&#xff1a;192.168.67.12 tomcat2&#xff1a;192.168.67.13安装nginx &#xff08;192.168.67.11&#xff09; #关闭防火墙和安全机制 [roottest1 ~]# systemctl stop firewalld [roottest1 ~]# setenforce 0#安装epel源 [ro…

iOS面试题锦集

1. 问&#xff1a;一张图片所占内存大小跟什么有关&#xff1f; 图片所占内存大小&#xff0c;与图片的宽高有关 我们平时看到的png、jpg、webp这些图片格式&#xff0c;其实都是图片压缩格式。通过对应的算法来优化了大小以节省网络传输与本地保存所需的资源。 但是当我们加…

【四 (1)数据可视化之如何选用正确的图表】

目录 文章导航一、数据分析中可视化的作用1、揭示数据关联和模式2、支持数据分析和决策3、提升沟通和共享效果4、强调关键信息和发现5、增强故事叙述和记忆效果6、有效增强数据交互性数据7、复杂信息易理解8、数据多维度显示 二、如何选用合适的图表1、简洁性避免使用过于复杂或…

【PTA】​L1-034 点赞(C++)

题目链接&#xff1a; L1-034 点赞 - 团体程序设计天梯赛-练习集 (pintia.cn) 目录&#xff1a; 目录&#xff1a; 题目要求&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 思路&#xff1a; 代码&#xff1a; 测试结果…

git下载及基础

git学习教程&#xff1a;【【GeekHour】一小时Git教程】 https://www.bilibili.com/video/BV1HM411377j/?share_sourcecopy_web&vd_source1a32dd27a726236a74603cf06b7302aa 1. 下载 官网&#xff1a;https://git-scm.com/ 终端输入 git -v 能看到版本信息表示安装成功…

最新开源解密版TwoNav网址导航系统源码

源码简介 2024最新开源解密版TwoNav网址导航系统源码去授权破解版 内置二十多套主题模板。 已去授权&#xff0c;最新开源解密版。TwoNav 是一款开源的书签&#xff08;导航&#xff09;管理程序&#xff0c;使用PHP SQLite 3开发&#xff0c;界面简洁&#xff0c;安装简单&…