深入理解指针【1】--(不一样的理解!!!)

9 篇文章 0 订阅
9 篇文章 0 订阅

内存和地址

在讲解指针之前,我们先讲一下内存和地址,为后面的指针讲解进行铺垫。

  • 内存:对于内存,我相信大家都不陌生。我们买电脑或者手机时都会关注它本身的内存大小,电脑中常见的内存大小8G/16G/32G等。内存很珍贵,容量越大价格也越贵,它的容量可没有1T这样的说法,1T这里只有硬盘才有的容量。大家要把内存和硬盘区分开,对于区分不开的人,可点击链接:内存和硬盘的区别
  • 内存的形象化理解内存就像学校的宿舍楼一样,宿舍楼里有很多分好的房间,而且每个房间你都可以住人,或者存放东西。内存也是一样,它也是被划分了很多小的空间,这些空间就跟房间一样,它里面可存放数据。我们都知道,房间有大小,存放的东西是有限制的。内存也是一样,它里面的每个空间都是有范围的,每个空间为一个字节的容量。 总结:内存被划分为很多的单元,每个单元为一个字节的容量,内存的存储容量单位为字节。如图所示:在这里插入图片描述
    在这里我们讲一下数据存储的单位和大小
1 byte = 8 bit
1 kb = 1024 bye
1 mb = 1024 kb
1 gb = 1024 mb
1 tb = 1024 gb
1 pb = 1024 tb
  • 地址:我们讲过了,内存就像一栋宿舍楼,一栋宿舍楼里面有很多的房间(寝室),而且每个寝室都有门牌号。如图所示:在这里插入图片描述
    当我们的朋友过来找我们玩的时候,假如每个寝室没有门牌号,他就需要一个一个房间的找,只有那样做才能最终找到你。虽然那也是一种方法,但是很不高效。但当我们有了门牌号,他就可以很迅速的找到你。内存也是如此,内存被划分为很多的一个一个的小单元,每个小单元都被进行了编码,这些编码就是门牌号,就是地址,而在C语言中给起了个新名字——指针。 所以,编码== 地址 ==指针内存通过这些门牌号(指针),对里面的空间单元进行数据的访问
  • 内存地址的理解:我们都知道,CPU(中央处理器)是数据处理的中心,跟我们的大脑一样。它从内存内读取数据,进行大量的运算(处理),然后把处理后的数据 (处理后的结果)再放回内存中。如图所示:在这里插入图片描述
    对内存进行数据的访问时,就要知道每个空间单元的地址。我们都知道,我们寝室门口会标注门牌号,以便被看到。但在内存中可不是这样被标注的。计算机是由很多个硬件组成的, 比如,在计算机里面有很多的电线,电子零件等。内存的地址就是由硬件设计的,它不会像门牌号那样进行标注,是由地址总线这个硬件设计好的。如图所示:在这里插入图片描述
    在CPU与内存之间有地址总线,数据总线控制总线组成。地址总线就是一根一根电线,每根数据线连接一个比特位。我们知道每个比特位储存0或1,也就是说每根地址线会向所对应的比特位,发0或1,也就是说每根线有两种状态(结果),即0或1。所以,两根线就是四种状态(结果),三根线就是八种状态(结果)……。地址总线的个数由计算机平台决定,在32位计算机中就有32根线,就有2的32次方状态(结果),在64位计算机中就有64根线,就有2的64次方状态(结果)。一台电脑生产好后,它里面的内存地址就已经通过地址总线这个硬件设计好了。就跟我们弹钢琴一样,钢琴的每个键上面并没有标注 ,do,re,mi,……si这些音符,但是每个键都对应一个音,就是因为在生产钢琴的时候,每个键对应什么音就已经设计好了。当CPU要处理某个空间单元的数据是,它就会先通过控制总线,通知内存它要读取数据了,然后再通过地址总线,向内存发送要读取的空间单元的地址,然后内存就会根据地址找到对应的空间单元,再把里面的数据通过数据总线传给CPU总结:编码 == 地址 == 指针

指针的应用

指针在计算机科学中具有广泛的应用。以下是一些常见的应用场景:

  • 内存管理:指针可用于动态分配释放内存。通过指针,可以在运行时分配和管理内存,使程序更加灵活和高效。

  • 数据结构:指针是实现各种数据结构(如链表、树、图等)的基本工具。通过指针,可以在内存中动态创建操作数据结构,实现高效的数据访问和操作。

  • 函数指针:函数指针是指向函数的指针变量,可用于在运行时动态选择和调用不同的函数。函数指针广泛用于实现回调机制事件处理动态加载等场景。

  • 数组访问:通过指针,可以直接访问和操作数组元素,提高数组访问的效率。

  • 字符串处理:指针可用于字符串的操作,如拷贝、连接、比较等。通过指针,可以对字符串进行高效的处理和操作。

  • 系统级编程:在操作系统底层编程中,指针是必不可少的工具。通过指针,可以直接访问硬件、进行内存映射、优化性能等。

  • 动态数据结构指针可用于实现动态数据结构,如动态栈、堆等。通过指针,可以在运行时分配和释放动态数据结构,提高程序的灵活性和效率

指针变量和地址

在了解完什么是内存,什么是指针(地址)后,我们回归C语言中。在以前的文章中,我们有讲过——变量的创建本质就是向内存申请空间。既然申请了空间,那就肯定有地址,所以怎样获取我们的变量地址呢?

  • &取地址操作符: 这个符号咱们前面也见过了,格式 :& 变量。进行代码展示:
创建一个变量,并用 & 取出变量的地址。
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	int a = 10;
	printf("%p\n", &a);
	return 0;
}

结果运行图:
在这里插入图片描述

  • 指针变量:前面,咱们讲过数据类型,比如,int , char ……指针变量也是个变量是专门用来存放指针(地址)的变量。竟然是变量,那就有数据类型,那么指针变量的数据类型是什么样的呢? 就是在数据类型后面加个* 比如,int * , char *,……。进行代码展示:
//  格式  : 数据类型  *  变量名  ;
int a = 10 ;
int * p = &a ;			//p里面存放的就是 a 的地址
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	printf("%p\n",p );
	return 0;
}

结果运算图:在这里插入图片描述
有个东西忘了告诉大家了——对于地址的打印用占位符%p。从结果运行图中能看出来,指针存放的就是变量的地址。同理,比如,char *……。大家可以自行试试。

  • 指针变量的大小:前面,我们有讲过,数据类型是有大小的,也就是因为有了大小,才会申请不同的空间。对于空间大小的测量,我们用sizeof测量(前面有讲过),进行代码展示:
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	printf("%d\n", sizeof(int*));
	printf("%d\n", sizeof(char*));
	printf("%d\n", sizeof(float*));
	printf("%d\n", sizeof(double*));
	return 0;
}

结果运行图:在这里插入图片描述
从结果运行图来看,不知道你有没有感到惊讶。不同的指针变量,它们的空间大小竟然相同!!!是不是感到很惊讶——它们占有的空间相同。我们先说结论:指针变量的大小与它的类型无关,只与所在的计算机平台有关,即,32位平台上占有4个字节,64位平台上占有8个字节。 接下来,我们进行解释:前面,我们讲过了地址总线,它的数据线数有计算机平台决定,它的每一根线都对应一个bit位,一个字节有8个bit,所以在32位的平台下就对应4个字节,在64位平台下对应8个字节。肯定有人有疑问——既然都一样,那为什么还要规定那么多指针数据类型呢?规定一个不就行了吗?。对于这个问题的解答,我们就要知道指针变量的真正含义,我们接下来讲。

  • 指针变量的解读:我们以整形指针变量为例子,比如,int a = 10 ;int * p = &a *号就说明了 p是个指针变量int 就说明了p所指变量a 里面的数据类型是个整形,指针变量存放的是地址,我们通过地址找到变量a里面的数据,就可以对其内容进行更改(就像根据门牌号找到了对应的房间,我们可拿或放入东西)。总结:指针变量就是通过地址找到对应的变量数据。如图所示:在这里插入图片描述
    接下来,我们通过代码进行展示:
// 创建一个变量,不通过直接更改内容,用指针进行更改。
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	int a = 10;
	printf("更改前a=%d\n", a);
	int* p = &a;
	*p = 30;
	printf("更改后a=%d\n", a);
	return 0;
}

结果运行图:在这里插入图片描述

  • 解引用操作符*就是解引用操作符p里面存放的是地址,我们通过*这个操作符对p里面的地址解读(通过 *和p里面的地址就找到了a里面的数据,就可以进行更改)。也就是说, * p==10。进行代码展示:
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	printf("a=%d\n", *p);
	return 0;
}

结果运行图:在这里插入图片描述
补充个知识点,地址的表示是十六进制,对于代码的研究具有可观性(后面就会体会到)地址的十六位进制表示,总共有8位活32位(由计算机平台决定),比如,0X00AFF9F4。因为在32位平台上,总共有32根线,一个十六进制位,可由4个bit位表示,所以只要8个十六进制位就OK了,依此推理64位平台。读到这里,我想有些人肯定有疑惑——对a的值进行更改,我们完全可以直接写成a = 30 ;为什么还费事的要用指针呢?其实,对于某个变量进行值的更改,通过直接更改是一种手段,通过指针进行更改是另一种手段。这两东西就相当于我们的工具,工具多了,我们解决问题的方法就多了。其实,通过指针进行变量的更改是很方便的,这个特征我们以后就会感受到的。

指针变量类型的意义

前面,我们已经讲过了,指针变量的大小与指针变量类型无关,只与计算机平台有关。这里我们就来讲一下,为什么要有那么多指针变量。这里我们直接说结论:指针变量的类型决定了能访问的空间大小。比如,int * 能访问4个字节空间,char *能访问1个字节空间。进行代码展示:

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	int a = 0x11223344;
	int* p = &a;
	*p = 0;
	printf("%d\n", a);
	return 0;
}

结果运行图:在这里插入图片描述

  • 指针变量+ -整数指针变量是能+ -整数的,想不到吧。我们直接上代码:
#define  _CRT_SECURE_NO_WARNINGS	1
#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	char* pc = (char*)&a;
	printf("  p=%p\n", p);
	printf("p+1=%p\n", p+1);

	printf("  pc=%p\n", pc);
	printf("pc+1=%p\n", pc + 1);

	return 0;
}

结果运行图:在这里插入图片描述
从结果运行图中,我们可以看出来,int *的指针变量对应的p当p+1时,地址就加了4个字节,char *的指针变量对应的pc,当pc+1时,地址就加了1个字节所以不同的指针变量类型,对应着指针变量加减时,在地址上加减多少个字节指针变量不只是可以加减1,可以加减任意数字。地址加多少就等于数字乘以对应的变量类型对应的字节

const修饰指针

const的单词意思:常量,常属性在普通常量中(除了指针变量):被const修饰的变量是无法进行更改的,被const修饰的变量被称为常变量在C语言中,被const修饰的变量本质还是变量,只不过语法上规定它不可以进行更改,就跟常量似的但在C++中,被const修饰的变量,它的本质就被改变了,就变成了常量。进行代码展示:

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	int a = 10;
	  a = 30;
	printf("%d\n", a);
	return 0;
}

结果运行图:在这里插入图片描述
代码展示2:

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	const int a = 10;
	  a = 30;
	printf("%d\n", a);
	return 0;
}

结果运行图:在这里插入图片描述
在C语言中,const修饰的普通变量的特性是可以用变长数组来验证的。进行展示:

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	int a = 10;
	int arr[a];
	return 0;
}

结果运行图:在这里插入图片描述
代码展示2;

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	const int a = 10;
	int arr[a];
	return 0;
}

结果运行图:在这里插入图片描述

void * 指针变量

前面,我们讲过了,每个数据的变量类型都有相对应的指针变量类型。我们自己写代码,我们大概率不会乱用指针变量,或者我们自己清楚自己用了什么指针变量。但当我们接过来别人的代码后,对于别人所使用的指针变量类型可能不太清楚,这个时候就可以用 void *这个指针变量来接收。void 就是空,无的意思对于什么样的指针变量类型都能接收。进行代码展示:

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	int a = 10;
	char* d = "abbb";
	void* p = &a;
	void* b = &d;
	return 0;
}

结果运行图:在这里插入图片描述
大家要注意下,void * 所修饰的指针变量是不能直接被 *操作的。因为,void *什么指针变量类型都接收,所以它不能确定你要哪个,必须进行类型转换才能进行*。进行代码展示:

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	int a = 10;
	void* p = &a;
	*p = 40;
	printf("%d\n", a);
	return 0;
}

结果运行图:在这里插入图片描述
代码展示2:

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
int main()
{
	int a = 10;
	void* p = &a;
	*(int*)p = 40;
	printf("%d\n", a);
	return 0;
}

结果运行图:在这里插入图片描述

每章的彩蛋时刻!!!

https://www.bilibili.com/video/BV1LU4y1S7Ni/?spm_id_from=333.337.search-card.all.click&vd_source=7d0d6d43e38f977d947fffdf92c1dfad在这里插入图片描述
每章一句“在任何一个你没有察觉的时刻,包括现在,通过行动去改变命运的机会,一直都在。”感谢你能看到这里,点赞+关注+收藏是对我的最大鼓励,咱们下期见!!!

  • 28
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值