C:初识指针—学习笔记

news2024/11/15 13:40:18

目录

前言:

1、内存和地址

1.1 理解内存和地址

1.2 理解编址

2、指针变量和地址

2.1 取地址操作符:&

2.2 指针变量

2.3 如何拆解指针类型

2.4 解引用操作符(*)

2.5 指针变量的大小

3、指针变量类型的意义

3.1 指针的解引用

3.2 指针+-整数

4、void* 指针

结语:


前言:

谈及指针,大部分人只有一个感觉:太难学了,好抽象啊!但是,请不要着急,今天当你看完这一篇后,相信你肯定能够理解什么是指针了。


1、内存和地址

1.1 理解内存和地址

在介绍指针前,我们需要先了解什么是内存和地址。

关于内存和地址,生活中有一个例子可以很好的解释它们

比如说你住在一栋宿舍楼,大楼内有100个房间,但是房间并没有编号。这时,你的一个朋友来找你玩,如果想找到你,就得一个房间一个房间的寻找,这样效率很低。但是,如果我根据楼层和楼层的房间的情况,给每一个房间都编上号,比如:

1楼:101 102 103……

当有了门牌号,这时候你只需要将门牌号告诉你朋友,他就可以很快速的找到房间,找到房间里的你。

如何将上面的例子抽象到计算机里呢?你可以理解宿舍楼就是内存,房间就是内存中的一个内存单元,房间里的你就是数据,而门牌号就是地址。

所以内存就是存储数据的空间

我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那我们买电脑的时候,电脑上内存是8GB/16GB/32GB等,那这些内存空间如何高效的管理呢?

cpu从内存中读取数据,就好比是你朋友要在宿舍楼里找到你,而你朋友找你,也只是在一个个房间寻找。大楼里的一个一个房间的划分,让人们对大楼内的面积能够充分利用,而计算机中也是如此。内存的空间只有8GB/16GB/32GB,因此对于内存的合理运用也变的很重要。

计算机中把内存也划分为一个个的内存单元,每个内存单元的大小取1字节。

补充)计算机中常见的单位:

一个bit可以存储一个2进制位的1和0

地址(门牌号)内存(大楼)其中,每个内存单元,相当于⼀个学⽣宿舍,一
个字节空间里面能放8个比特位,就好比同学们住
的八⼈间,每个人是⼀个比特位。
0xFFFFFFFF(16进制1个字节
0xFFFFFFFE1个字节
1个字节

每个内存单元也都有⼀个编号(这个编号就相当
于宿舍房间的门牌号),有了这个内存单元的编
号,CPU就可以快速找到⼀个内存空间。

内存单元

生活中我们把门牌号也叫地址,在计算机中我们

把内存单元的编号也称为地址,
C语言中给地址起了新的名字叫:指针

1个字节
0x000000011个字节
0x000000001个字节

所以我们可以理解为:
内存单元的编号 == 地址 == 指针

1.2 理解编址

生活中关于我们可以看到通过宿舍门上的门牌号,直接找到我们想去的地方。门牌号是真实存在与宿舍门上的。而内存中的地址我们该怎么理解呢?

计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的。

这就像钢琴,吉他上面没有刻上“剁、来、咪、发、 唆、拉、西”这样的信息,但演奏者照样能够准 确找到每⼀个琴弦的每⼀个位置,这是为何?因为制造商已经在乐器硬件层面上设计好了,并且 所有的演奏者都知道。本质是⼀种约定出来的共识!

有点抽象,举个例子:

图书馆里有一排排的书架,每个书架又有一层层的格子,这些格子就好比内存中的存储单元。

给每个格子编上号,也就是编址,就像是要给图书馆里的每个格子都贴上标签。

那这个标签是怎么贴上去的呢?这就得靠图书馆的“硬件设计”了。

比如说,书架的排列方式、格子的划分规则,就像是硬件的设计。

想象一下,书架是固定的,它们的位置和大小决定了格子的位置和数量,这就好比硬件决定了内存有多少个可以存储数据的地方。

然后,有一套专门的标记系统,就像特殊的机器或者装置,按照书架和格子的排列,给每个格子都印上编号,这就是通过硬件实现了编址。

那硬件设计又是怎么实现的呢?

首先,必须理解,计算机内是有很多的硬件单元,而硬件单元是要互相协同工作的。所谓的协 同,至少相互之间要能够进⾏数据传递。 但是硬件与硬件之间是互相独立的,那么如何通信呢?答案很简单,用"线"连起来。 ⽽CPU和内存之间也是有大量的数据交互的,所以,两者必须也用线连起来。通过地址总线,我们就可以了解什么是硬件编址了。

32位机器有32根地址总线, 每根线只有两态,表示0,1【电脉冲有无】,那么⼀根线,就能表示2种含义,2根线就表示4种含义,依次类推。32根地址线,就能表示2^32种含义,每⼀种含义都代表⼀个地址。 地址信息被下达给内存,在内存上,就可以找到 该地址对应的数据,将数据在通过数据总线传入CPU内寄存器。

内存编址这件事,是靠计算机里面那些实实在在的硬件设备,按照一定的规则和办法来做好的。

简单来说,计算机的编址是通过硬件设计把每一个内存单元的地址都固定好了,不需要把地址额外的存起来。

知识补充:

  1. 32位机器有32根地址总线64位机器有64根地址总线。
  2.  地址总线是实际存在的物理电线

2、指针变量和地址

当我们了解了内存和地址的关系后,就可以开始对指针的学习啦!

在C语言中变量创建的本质是向内存申请空间

比如说:int a = 10;

这串代码就相当于向内存内申请了4个字节,一个整型占4个字节,这串空间我们想存放的数据便是变量10。

2.1 取地址操作符:&

int main()
{
	int a = 0x11223344;//16进制数字
	return 0;
}

16进制0x11223344,一个16进制位可以改写为4个二进制位,因此,11223344可以改写为32个二进制位表示,刚好一个整型可以放下。

我们可以调试来看一下内存:

打开后,输入&a 并敲下回车键,列改为一行(记得在x86环境下观察,比较方便)

我们可以看到44,33,22,11都各占一个字节,每一个字节都有一个地址。

我们将列数改为4列再观察

从上面我们可以看到a确实向内存申请了4个空间。

读到这我们可能会有一个新的问题,欸,4个字节都有地址,那我们怎么知道a的地址是哪一个呢?

还记得前面调试的时候我们是怎么观察地址的吗?我们通过输入&a按下回车后出现了0x00E2FEC4,因此0x00E2FEC4便是a的地址,我们也可以发现这个地址和 44 所占字节的地址一样。

总结一下,&a取出的是a所占4个字节中地址较小的字节的地址

虽然整型变量占用4个字节,但是我们只要知道了第一个字节地址,顺藤摸瓜访问到4个字节的数据也是可行的。

代码展示一下,怎么打印地址

int main()
{
	int a = 0x11223344;
	printf("&a=%p\n", &a);
	return 0;	
}

知识补充:

  • &是取地址操作符,想要得到地址,就需要使用这个操作符
  •  %p:用来打印地址的占位符

结果展示:

和前面调试的结果不是一样,是因为在打印的时候又申请了新的空间。

2.2 指针变量

在我们之前学习的过程中,我们如果想要将一个整数存储起来,就会创建一个整型变量,比如我们想存储数字10,如下代码:

int main()
{
	int a = 10;
	return 0;	
}

我们通过创建了一个整型变量 a 来存储10。

那么如果我们想要将我们通过&得到的地址存储起来,有没有什么办法呢?我们可以将地址存储在指针变量中。比如我们想要存放 n 的地址

int main()
{
	int a = 10;
	int * pn = &n;
	return 0;	
}

解读:int * pn = &n;

1、pn被称为指针变量

为什么呢?

&n——n的地址——地址就是指针

pn = &n;

pn就是用来存放地址的,也可以说是用来存放指针的

指针变量就是存放指针的变量。

可以通过和整型变量来理解指针变量,

整型变量:a就是用来存放整数的。

2、int * 被称为指针类型

int a = 10;
int * pn = &n;

对比就可以发现,int是整数的类型,int*是指针的类型。

2.3 如何拆解指针类型

上文说到了之指针的类型是(int *),那么我们该如何理解指针类型呢?

pn的类型是int *,我们需要分别理解

  • *:说明pn是指针变量
  • int:说明pn指向的对象是int类型
char ch = 'x';

如果我们想要存放x的地址该怎么写呢?

 char * pc = &ch;

这样我们就存放了x的地址

告诉我们pc是指针

char则告诉我们指针指向的对象是char类型。

2.4 解引用操作符(*)

当我们将地址保存起来后,是为了后面能够使用,那么我们该怎么使用呢?

在现实生活中,我们使用地址要找到⼀个房间,在房间里可以拿去或者存放物品。

C语言中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象,这里必须学习⼀个操作符叫解引用操作符(*)。

int main()
{
	int n = 10;
	int * pn = &n;
    //解引用操作符(间接访问操作符)
	*pn = 100;
	printf("%d", n);
	return 0;	
}

上述代码中的*pn就使用了解引用操作符, *pn 的意思就是通过pn中存放的地址,找到指向的空间, *pn其实就是n变量了;所以*pn=100,这个操作符是把n改成了100.

从结果上看,n的值的确被改为了100

或许通过这个例子你会觉得指针有是什么用?如果只是想修改n的值,为什么不直接写一个n=100呢?这样不是更方便吗?

其实这里是把n的修改交给了pn来操作,这样对n的修改,就多了⼀种的途径,写代码就会更加灵活, 后期慢慢就能理解了。

其实这里有一个很好的例子能说明指针的作用,生活中,有些事情是不方便自己去做的,因此呢,就需要委托别人来代替你做,比如说一个老板想要喝奶茶,但是他不会自己顶着大太阳出去买,而是会吩咐他的秘书取帮他完成,差不多就是这样,可能有些不恰当,见谅哈!

2.5 指针变量的大小

我们知道我们创建一个整型变量int的大小是4个字节,字符变量char的大小是1个字节,那么指针变量的大小又是多少呢?

思考过程:

指针变量存放的是地址,地址的存放需要多大的空间呢?知道地址存放的空间就是指针变量的大小

也就是说指针变量的大小取决与地址的大小

通过前面的内容我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后 是1或者0,那我们把32根地址线产生的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4个字节才能存储。

如果指针变量是用来存放地址的,那么指针变的大小就得是4个字节的空间才可以。

同理64位机器,假设有64根地址线,⼀个地址就是64个二进制位组成的⼆进制序列,存储起来就需要 8个字节的空间,指针变量的大小就是8个字节。

int main()
{
	int n = 10;
	int * pn = &n;
	printf("%zd\n", sizeof(pn));
	return 0;	
}

在32位机器上: 可以看到打印的大小是4个字节

在64位机器上: 可以看到打印的大小是8个字节

那么指针类型是否会影响指针变量的大小呢?我们来测试一下

int main()
{	
	printf("%zd\n", sizeof(int*));//整型
	printf("%zd\n", sizeof(char*));//字符
	printf("%zd\n", sizeof(short*));//短整型
	printf("%zd\n", sizeof(double*));//双精度浮点型
	return 0;	
}

在32位系统上结果:

在64位系统上结果

我们可以看到,不管指针类型是什么,都不会影响指针变量的大小,指针类型的变量大小,在相同平台下,大小都是相同的。

总结:

  •  32位平台下地址是32个bit位,指针变量大小是4个字节
  •  64位平台下地址是64个bit位,指针变量大小是8个字节 X64环境输出结果
  •  注意指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。

3、指针变量类型的意义

既然指针变量大小与指针类型无关,那么为什么还要搞指针的变量类型呢?

3.1 指针的解引用

int main()
{
	int n = 0x11223344;
	int* pi = &n;
	*pi = 0;
	return 0;
}

调试过程:(注意观察内存里的值

运行到292行时内存展示44 33 22 11

当经过*pi = 0 之后

内存里4个字节全部变为了 0 

如果我们不用int *的指针类型,改为char *的类型,结果又是如何呢?

我们可以看到只是将n的第⼀个字节改为0。

我们可以看到int类型指针可以访问4个字节,而char类型指针只访问了1个字节。

通过对比,我们可以得到一个结论

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

3.2 指针+-整数

int main()
{
	int n = 0x11223344;
	int* pi = &n;
	char* pc = &n;		
	printf("&n = %p\n", &n);
	printf("pi = %p\n", pi);
	printf("pi+1 = %p\n", pi+1);
	printf("pc = %p\n", pc);
	printf("pc+1= %p\n", pc+1);
	return 0;
}

结果: 

我们可以发现, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。 这就是指针变量的类型差异带来的变化。指针+1,其实跳过1个指针指向的元素。指针可以+1,那也可以-1。

结论:指针的类型决定了指针向前或者向后走一步有多大(距离)。

4、void* 指针

void的意思是无,或者空

所以void*指针是无具体为无具体类型的指针(或者叫泛型指针),这种类型的指针可以用来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进行指针的+-整数和解引用的运算。 

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

在上面的代码中,将⼀个int类型的变量的地址赋值给⼀个char*类型的指针变量。编译器给出了⼀个警告(如下图),是因为类型不兼容。而是用void*类型就不会有这样的问题。


使用void*类型的指针接收地址就不会出现警告

void* 类型的指针不能直接进行指针的+-整数和解引用的运算

int main()
{
	int n = 10;
	void* pc = &n;
	*pc = 20;
	return 0;
}

当我们想运行的时候,就会报下面这个错误 

 void* 类型的指针可以接收不同类型的地址,但是无法直接进行指针运算。

这是因为void*是一个无具体类型的指针,当进行解运算的时候,没法确定访问几个字节。

既然如此,那void*有什么作用呢?

专门用来存放别人传送过来的地址,当你不知道别人给你传的是什么类型的指针的时候,就可以使用void*来存放,当需要进行解运算的时候,在使用强制类型转换来实现。

比如:

int main()
{
	int n = 10;
	void* pc = &n;
	*(int*)pc = 20;
	return 0;
}

 这样就将类型强制转换成了整型指针,结果就可以打印出来了


结语:

本篇文章主要讲了指针的基本知识,通过本篇文章能够了解什么是指针,指针变量,指针类型是什么。后面会继续更新指针相关知识,希望能够帮助大家攻克指针这一模块。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

U盘文件或目录损坏无法读取?详解两大恢复策略

在日常的数字生活中,U盘作为便携的数据存储工具,承载着我们的重要文件与回忆。然而,当U盘中的文件或目录突然变得无法读取时,这份便捷瞬间转化为困扰。U盘文件或目录的损坏,往往是由于多种因素共同作用的结果&#xff…

【CSDN平台BUG】markdown图片链接格式被手机端编辑器自动破坏

bug以及解决方法 现在是2024年8月,我打开csdn手机编辑器打算修改一下2023年12月的一篇文章,结果一进入编辑器,源码就变成了下面这个样子,我起初不以为意,就点击了发布,结果图片全部显示不出来了。 而当我修…

FPGA:串口通信发送模块

FPGA:串口通信发送模块 1、串口通信的概念及分类(1)串口通信概念(2)串口通信分类 2、UART协议(1)FPGA实现UART协议发送模块思路(2)Verilog设计文件a.波特率选择模块b.单个比特发送模块c.位状态计数器d.延时计数器e.数据保存寄存器f.位发送逻辑g.led翻转逻辑h.使能信号en_send逻…

鸿蒙系统开发【ASN.1密文转换】安全

ASN.1密文转换 介绍 本示例对使用kit.CryptoArchitectureKit加密后的密文格式进行转换。kit.CryptoArchitectureKit加密后的密文格式默认为以base64显示的ASN.1格式问题,通过对密文进行base64变换后得到字符数组,以16进制数字显示,再此基础…

看完这几本大模型书籍,你就是LLM大师,非常详细收藏我这一篇就够了

以下是几本关于大模型和人工智能领域的经典书籍,它们各自具有独特的特点和适用人群: 《深度学习》(Deep Learning) 作者:伊恩古德费洛(Ian Goodfellow)、约书亚本吉奥(Yoshua Bengi…

刀具磨损预测工器具磨损预测-RIME-CNN-SVM霜冰算法优化-完整代码数据

直接看项目演示: 刀具磨损预测工器具磨损预测-RIME-CNN-SVM霜冰算法优化_哔哩哔哩_bilibili 效果演示: 代码: import numpy as np import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torch.utils.data import DataLo…

算法第十七天:leetcode242.有效的字母异位词

一、有效的字母异位词的题目描述与链接 242.有效的字母异位词如下表所示,您可以直接复制下面网址进入力扣学习,在观看下面的内容之前您一定要先做一遍哦,以便让你印象更加深刻! https://leetcode.cn/problems/valid-anagram/desc…

curl wget介绍

0 Preface/Foreword 1 curl介绍 curl:transfer a URL synopsis: curl [options] [URL...] 描述: curl is a tool to transfer data from or to a server, using one of the supported protocols (DICT, FILE, HTTP, HTTPS, IMAP, FTP, FT…

【JavaEE初阶】懒汉模式与饿汉模式及指令重排序问题

目录 📕 单例模式 🌳 饿汉模式 🚩 线程安全 🎍 懒汉模式 🚩 懒汉模式-单线程版 🚩 懒汉模式-多线程版 🎄 指令重排序 📕 单例模式 单例模式是一种经典的设计模式,…

Python | Leetcode Python题解之第312题戳气球

题目: 题解: class Solution:def maxCoins(self, nums: List[int]) -> int:n len(nums)rec [[0] * (n 2) for _ in range(n 2)]val [1] nums [1]for i in range(n - 1, -1, -1):for j in range(i 2, n 2):for k in range(i 1, j):total v…

文档编辑协作神器,全方位解决方案,灵活部署赋能个人与企业-onlyoffice

文章目录 1. 引言2. 免费畅享,个人的编辑利器2.1 PDF直接修改功能2.2 插件支持,应对多种工作场景2.3 AI助手智慧办公 3. 私有部署,开发者的自定义编辑器3.1 开发者版本介绍3.2 私有部署,解决数据安全问题3.3 实用功能丰富&#xf…

基于SpringBoot+Vue的流浪猫狗救助救援网站(带1w+文档)

基于SpringBootVue的流浪猫狗救助救援网站(带1w文档) 基于SpringBootVue的流浪猫狗救助救援网站(带1w文档) 该流浪猫狗救助救援网站在Windows平台下完成开发,采用java编程语言开发,将应用程序部署于Tomcat上,加之MySQL接口来实现交互式响应服…

LKEB-4030电阻器LKEB4030模块可议价

LKEB-4030电阻器LKEB4030模块可议价 LKEB-4030电阻器LKEB4030模块可议价 LKEB-4030电阻器LKEB4030模块可议价 LKEB-4030电阻器LKEB4030模块说明书 LKEB-4030电阻器LKEB4030模块接线图 LKEB-4030电阻器LKEB4030模块线路图 LKEB-4030电阻器(Resistor&#xff09…

新手如何参加护网行动?一篇带你零基础入门到精通

前言 1、什么是护网行动 “护网行动”是指国家组织的网络安全防护演练行动。 护网行动通常由国家相关部门组织,旨在模拟真实的网络攻击情况,检验和提升关键信息基础设施、重要信息系统和大数据的安全防护能力。 在护网行动中,会有专业的攻…

基于微信小程序的搬家服务系统(源码+lw+部署文档+讲解等)

博主介绍:✌全网粉丝10W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 涉及技术栈:SpringBoot、Vue、SSM、H…

SEO域名篇—如何选择一个好域名

SEO 域名选择误区 很多做站的大佬都告诉新入行的SEO优化员,选择域名一定要选择老域名,这里也是一个误区,很多新手在听了大佬的话后去就选择一些5年-15年的老域名直接做站,不去查查这个域名是否被360、QQ、微信等等一切搜索引擎拦…

Unity | Shader基础知识(第二十集:应用-简易流光、LOD)

目录 一、前言 二、LOD 1.什么是LOD 2.代码如何调节LOD 三、流光 1.资源准备 2.uv移动 3.获取图片中的uv 4.改变uv去取流光的颜色(时间的应用) 5.图片叠加 6.透明图片的叠加 四、纯净代码 五、作者的碎碎念 一、前言 有小伙伴问&#xf…

使用腾讯云域名解析实现网站重定向

前言 最近,在CSDN平台上我写了一系列博客,希望能与同学分享一些技术心得。然而,每当需要向他人推荐我的博客时,那串复杂且缺乏规律的CSDN博客首页域名总让我感到不便。这让我开始思考,如果能将这一域名替换为一个既个…

技术速递|VS Code Java 7月更新 - Gradle 支持增强!用户体验改进与 Spring 新功能

作者:Nick Zhu 排版:Alan Wang 大家好,欢迎来到 Visual Studio Code 7月份的 Java 更新!本博客将覆盖最新的 Gradle 功能增强、用户体验改进以及新的 Spring 功能,让我们开始吧! Gradle 功能增强 支持 Gr…

算法强训day19

一、小易的升级之路 链接&#xff1a;小易的升级之路_牛客题霸_牛客网 简单题 #include<iostream> using namespace std; long long gcd(long long m, long long x) {long long n ;while(x>0){n m % x;m x;x n;}return m; } int main() {int n;long long m;cin &…