发布时间:2024-12-12 19:01
今天,我们来讲一下指针和结构体
都说指针很难,其实很好理解的,我来带你了解一下
说指针就不得不说一下内存
内存是电脑上特别重要的存储器
我们可以打开任务管理器
在任务管理器里面可以看到内存的使用情况,有每个软件运行所占的内存,可以看到计算机中程序的运行都是在内存中进行的
但是我们需要有效的使用内存空间,这里我们买个
4G
内存的电脑,可以把4G
的内存分成一个一个的小空间,这样就会方便管理
就像这样,
这是一大块儿的内存,我们要怎么去管理它呢?
类比一下生活,就像我们的国家,这么大的一个国家我们要怎么管理呢?你会想到省,县,镇,村,把一大块的国家分成一个省一个省管理,每个省都有自己的管理制度,但是服从于中央管理,内存也是这样,也会划分成这样一个一个的小格子,叫做内存单元,一个内存单元的大小是一个字节
来一个宿舍楼,叫13栋宿舍楼(因为我住的就是13栋,哈哈)
里面有很多的房间,但是都没有编号,我买了个外卖,我说有一个房间是我住的,送外卖的也不知道我在那个房间,只能一个一个的去找.
但是我给每一个房间都编上号,像这样
我说我在305号宿舍,送外卖的是不是唰的一下就找到我了
内存就是13栋宿舍楼,内存单元就是房间,内存这么大的一块儿空间,分成一个一个的内存单元,每一个内存单元都有一个地址,这个地址就是房间的编号,是不是就能很好的管理内存了
我们再来说一下地址线
我们的电脑有32位
的,也有64位
,这里指的是有32
条地址线和64
地址线,地址线通电就会产生脉冲,每一个地址线都有两种状态,也就是二进制里面的1/0
这里就说32位
的电脑
这里有
32
根的地址线
每根地址线都有1/0
的选择
它的组合有:
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0001
0000 0000 0000 0000 0000 0000 0000 0010
…………
1111 1111 1111 1111 1111 1111 1111 1110
1111 1111 1111 1111 1111 1111 1111 1111
一共有223种,也就是
4,294,967,296
种
所以有223个编号,对应223个地址
4,294,967,296byte
4,294,967,296÷1024=4,194,304kb
4,194,304÷1024=4,096MB
4,096÷1024=4GB
所以
32位
的电脑可以访问4GB
大小的空间
我们现在常见的都是
64位
的电脑,类比上面,可以知道它可以访问很大的空间,它是有这个能力的
我们认定
1
个地址管理1
个字节的大小
但是为什么不是一个bit
呢?
之前我们讲过计算机最小的单元是bit(比特位)
,1
个字节=8
个bit
我来解释一下
创建一个数据类型里面最小的字符类型
char a;
,这个a
的大小是一个字节,
如果我们一个地址只对应一个
bit
,但是一个最小的char
类型的变量a
就占8
个地址,是不是太浪费了
就像送快递,我在13栋305宿舍,你非得给我送到13栋305宿舍第5块瓷砖上,是不是没有必要
这样有什么用呢?
来看一下代码的运行过程
int main()
{
int a = 0;
&a;
return 0;
}
这里的
int a = 0;
的意思就是向内存申请4
个字节的空间来存放0,
&
是取地址操作符(之前没有讲),它的作用是把a
这个变量的地址给取出来
我们来调试一下
(如何调试看这一篇博客的问题三)
打开监视(在调试开始的时候才有)
调试->窗口->监视->输入监视的变量
看到没有,a
被创建了,&a
也把地址取了出来,它的地址是0x009dfa5c
,这个地址里面存储了10
这个值,
我们再打开内存(在调试开始的时候才有)
调试->窗口->内存
把
自动
改为4
把a
的地址0x009dfa5c
填在里面,回车,就会查到这个地址
红框就是地址,橙框就是内存中的数据,紫框就是内存数据的文本分析(编译器想尝试分析一下你里面存的是什么东西,很明显,是不准确的)
来说一下橙框里面的
0a 00 00 00
这个是十六进制
a
代表的就是存储的值10
你发现没有,红框里地址
0x009dfa5c
下面的不是0x009dfa5d
,而是0x009dfa60
,相差3
个地址
橙框里0a
就是地址0x009dfa5c
,后面的00 00 00
分别是0x009dfa5d 0x009dfa5e 0x009dfa5f
这三个地址没有写,
我们开辟了
4
个字节,分配了四个地址,为什么a
的值存在第一个而不是后面呢?以后我们会讲到
之前我们存的是
a
的值,那么我们怎么把&a
取出来的地址存起来呢?
int main()
{
int a = 10;
int* p = &a;
return 0;
}
int* p
就是指针变量p
,跟之前讲的那些变量一样,都是变量,指针变量p
可以把&a
取出来的地址存起来
在int* p
中p
指向的就是a
的地址,而*
就是说明p
是一个指针,int
指的是p
所指向a
的地址所存放的类型是int
类型
int main()
{
char a = \'w\';
char* p = &a;
return 0;
}
char* p
的char
就是指针变量p
所指向a
的地址所存放的类型是char
类型
我们每一次运行的时候,变量
a
存的地址都是不一样的,而且地址我们是不能改的,编译器已经把地址给你分配好了,不能随便改,这一点要记住(用这个printf(\"%p\",p);
打印一下试试看,看看地址是不是在变化)
有一句话,在锤子眼里,什么都是钉子
指针变量眼里,你放在里面的都是地址,它只能用来存放地址
既然我们把这个
a
的地址存在了指针变量p
中,那么我们可以通过这个地址来找到它所指向的对象吗
我们用*p;
来进行这个过程
这里的*
叫解引用操作符,意思是通过指针变量p
存放的地址,来找到p
指向的对象,即*p
就是a
总结一下
&
和*
这两个操作符,它们是一一对应的
&
是取出a的地址,*
是根据地址重新回到a
,一来一去的
试一试:
#include
int main()
{
int a = 10;
int* p = &a;
*p = 20;//这里的*p其实就相当于a了
//打印a的地址和a的值
printf(\"%p\\n\", p);//打印地址用%p
printf(\"%d\\n\", a);
return 0;
}
运行的结果:
008FFD9C
20
可以看到,最开始赋予a
的值是10
,但是把20
赋予*p
后,打印的a
的值是20
,所以就能看出来*p
其实就是变量a
了
之前我们用
sizeof
计算了char,short,int,long,float,double
的大小,分别是1,2,4,4-8,4,8
那么我们来计算一下指针变量的大小
int main()
{
printf(\"%zu\\n\", sizeof(char*));
printf(\"%zu\\n\", sizeof(short*));
printf(\"%zu\\n\", sizeof(int*));
printf(\"%zu\\n\", sizeof(long*));
printf(\"%zu\\n\", sizeof(float*));
printf(\"%zu\\n\", sizeof(double*));
return 0;
}
运行的结果:
4
4
4
4
4
4
为什么会是这样,它们指向的类型不是不一样吗?
不管创建什么类型的指针,都是在创建指针变量,指针变量是用来存放地址的
而指针变量的大小取决于一个地址存放的时候需要多大空间
在32位
的机器上的地址有32
个bit
位,也就是4
个byte
在64位
的机器上的地址有64
个bit
位,也就是8
个byte
我们刚才是在
32位
平台上运行的,所以大小是4
个字节
我们改一下,改成
x64
,也就是64位
的平台
再运行一下:
8
8
8
8
8
8
看到没有,指针变量的大小只跟在哪个平台上有关,在32位
平台就是4
个字节,在64
位平台上就是8
个字节
写指针变量有两种形式
int a = 10;
int* p = &a;
int *p = &a;
你可以把
*
向左贴,也可以向右贴,这都是可以的,这个是写代码的习惯,各有优点。
比如,你看看这行代码
int* p1, p2, p3;
你认为
p1,p2,p3
都是指针变量吗?
不,当然不是,这个*
其实只给了p1
,只有p1
是指针变量,而p2,p3
都只是跟着int
,没有分配*
,所以它俩都是整型
我们这样写
int *p1, p2, p3;
是不是能很好的理解了,
*
只贴向p1
,所以只有p1
是指针变量
我们要把它们全部变成指针变量,就这样做
int *p1, *p2, *p3;
所以它们各有优点,看个人习惯
指针就学到这里吧,简单了解一下就行了
springboot 正确的在异步线程中使用request的示例代码
OSError: [WinError 127] 找不到指定的程序
paper阅读笔记(Transformer):Attention Is All You Need
JAVA中stacksize是什么意思_深入理解JS函数stack size计算方法
r语言python对比_R语言和Python语言在数据科学方面的比较:当今进展的总结
《机器学习》周志华(西瓜书)学习笔记 第二章 模型评估与选择
跟我做⼀个高德地图的 iOS / Android MAUI 控件(前言)