文章目录
前言
为了后面的指针学习,小编有两个内容在前言中给各位读者讲述一下
第一个内容
在学校生活中,随着时代的发展,接收快递是必不可少的,为了更加便捷的服务,快递增加了送货上门的服务,假设快递员送货上门并在你宿舍楼下,该怎么送到你的宿舍门口呢?毫无疑问就是你的
门牌号
。在每一个宿舍门口上方都会贴有门牌号,一楼第一个房间叫101,二楼第二个房间叫202,依次类推给每一个房间都贴有门牌号,门牌号在快递员的眼中就是你现在所处地方的地址
,快递员根据地址找到你的位置,并把快递送到你的手中。
第二个内容
计算机中常见的单位
一个比特位可以存储一个二进制的位1或者0
也就是说在计算机中,数据都是用数字1和0进行存储的,而存储数字1和0的最小单位我们称作比特
了解完上述内容我们可以学习下方内容了
提示:以下是本篇文章正文内容,下面案例可供参考
一、内存和地址
1.1 内存
什么叫内存?
我们买手机买电脑的时候大家是不是都会关注买手机要买多大的,是16+256GB还是16+512GB,这个就是我们所要讲述的内存。
在计算机中,内存就是存储我们产生的数据的,当cpu在处理数据的时候,我们需要从内存中读取数据,cpu处理完后的数据也会放到内存当中。这么多的数据都存放在内存当中,在实际我们想要使用这个存储的数据的时候我们该怎么高效的使用他呢,总不可能挨个的去寻找,设计当初就考虑到了这个问题,根据我们课程准备前言中快递员送快递的例子我们来讲一下数据在内存中的存储。
和快递员送快递一样的,在内存中,内存也划分了一个一个的内存单元,一个内存单元占一个字节,就好比我们的学生宿舍一样,宿舍住着8个人,一个内存单元存储8个二进制位也就是一个字节。和宿舍一样的,计算机给每个内存进行了编号,这个编号就相当于宿舍的门牌号,也就是地址,在c语言中,我们给地址取了一个新的名字叫做指针,因此可以认为:编号==地址==指针
1.2 内存中的编址
在计算机中存在很多硬件单元,硬件单元和硬件单元要相互配合协调工作,才能使整个计算机运行下来。那硬件单元和硬件单元之间怎么相互配合呢?学过单片机的读者可能直到,硬件单元和硬件单元之间靠线进行链接,如下图:
在计算机中,从内存中读取数据还是传递数据,通过控制总线
写还是读,当从内存中读取数据,cpu通过地址总线
找到内存中存储该数据的地址,找到该数据,然后通过数据总线
将该数据传送给cpu,这样就完成了数据的调用。
CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,⽽因为内存中字节很多,所以需要给内存进⾏编址(就如同宿舍很多,需要给宿舍编号⼀样)。但是计算机不能把所有的地址都存到cpu中,因此cpu存在地址总线,32位机器下有32根地址总线,一根地址总线可以发出1和0的信号,也就是说32根地址总线可以有2的32次方个地址输出含义,每一个含义都代表一个地址。
地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传⼊CPU内寄存器。
二、指针变量和地址
2.1 取地址操作符(&)
c语言中创建变量就是像内存申请空间
#include<stdio.h>
int main()
{
int a = 10;
return 0;
}
在这里我们创建了一个整型的变量a,a要存放在内存当中,整型变量占四个字节的空间,内存申请了四个字节的空间用来存放整型a,如上图红色的,上面的地址是用十六进制表示,两个十六进制位也就是8位数据,也就是一个字节。上图中,整型变量a占了四个字节的空间,每个字节都有相应的地址,上面用红色框起来的就是每个字节的地址。
那我们怎么取出a的地址呢?这里就需要学到一个操作符叫取地址操作符(&)
#include<stdio.h>
int main()
{
int a = 10;
& a;
printf("%p\n", &a);
return 0;
}
&a,取出a的地址,用%p打印出a的地址,我们可以发现a的地址就是a所占四个字节中地址最小的那个地址,所以&a取出的是a所占4个字节中地址较⼩的字节的地址。虽然整型变量占⽤4个字节,我们只要知道了第⼀个字节地址,顺藤摸⽠访问到4个字节的数据也是可行的。
2.2 指针变量和解引⽤操作符
2.2.1 指针变量
我们通过取地址操作符取出a的地址,比如0x0030fcdc,但是有时候,这个地址也需要存储起来放在内存中,这时候就需要一个变量——
指针变量
。
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;//取出a的地址并存储到指针变量pa中
return 0;
}
指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。
2.2.2 如何拆解指针类型
int* pa = &a;//取出a的地址并存储到指针变量pa中
在上面取出a的地址放在pa中,pa就是指针变量,pa指针变量的类型就是int*,在这里我们可以将int*分成两个部分。
- *代表这个变量是指针变量
- 因为pa存放的a的地址,a地址中存放的是整型变量,所以这里int表示整型的指针变量。
2.2.3 解引用操作符
在上面我们将地址存储起来放在pa中,但是后面我想使用这个地址该怎么办呢?
这时候我们就需要用到解引用操作符了*
,解引用操作符用法如下
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;//取出a的地址并存储到指针变量pa中
* pa = 10;
return 0;
}
在上面*pa = 0;* 这就是解引用操作符,解引用操作符又叫做间接访问操作符,*pa 通过pa指针变量中存储的地址找到pa所指向的对象,也就是变量a所开辟的那段空间,等价于变量a
2.3指针变量的大小
上面讲了指针变量是专门空来存储地址的,所以指针变量的大小和地址有关,地址是如何产生的呢?根据上述内容,地址是通过地址线产生的,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4个字节才能存储。所以在32位机器下,指针变量的大小就是四个字节,跟地址中存储的变量的类型无关。在这里我们可以用代码验证一下。
#include <stdio.h>
//指针变量的⼤⼩取决于地址的⼤⼩
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
printf("%zd\n", sizeof(char*));
printf("%zd\n", sizeof(short*));
printf("%zd\n", sizeof(int*));
printf("%zd\n", sizeof(double*));
return 0;
}
在这里可以看出,指针变量的大小与地址中存储的变量类型无关,他只取决于地址的大小,在32位平台下地址大小是4个字节,在64位平台下地址大小是8个字节。
三、指针变量类型的意义
指针变量的⼤⼩和类型⽆关,只要是指针变量,在同⼀个平台下,⼤⼩都是⼀样的,为什么还要有各种各样的指针类型呢?下面详细给大家叙述一下原因。
3.1 指针的解引用
在下面通过两段代码来解释指针变量类型的意义
#include<stdio.h>
int main()
{
int a = 0x11223344;
int* pa = &a;
*pa = 0;
return 0;
}
在上面代码调试我们可以看到,创建a变量开辟了四个字节的空间,取出a的地址存放在pa中,pa指针变量的类型是int*,此时通过解引用操作符,通过pa找到pa所指向的那块内存空间,也就是为a变量开辟的那块空间,改变a的值,所有四个字节全部改变。
#include<stdio.h>
int main()
{
int a = 0x11223344;
//int* pa = &a;
//*pa = 0;
char* pc = &a;
*pc = 0;
return 0;
}
在这个代码调试过程我们可以发现用char*类型存储指针变量pc,通过pc改变a的值只能改变一个字节的值,只能将一个字节改为0。
结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。
⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。
3.2 指针±整数
#include <stdio.h>
int main()
{
int n = 10;
char* pc = (char*)&n;
int* pi = &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;
}
我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。这就是指针变量的类型差异带来的变化
结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。