一、内存、地址、指针变量
在计算机中我们把内存单元的编号也称为地址
C语⾔中给地址起了新的名字叫:指针。
所以我们可以理解为: 内存单元的编号 == 地址 == 指针
1.1 取地址操作符(&)
理解了内存和地址的关系,我们再回到C语⾔,在C语⾔中创建变量其实就是向内存申请空间,⽐如:
#include <stdio.h> int main() { int a = 10; return 0; }
上述的代码就是创建了整型变量a,内存中申请4个字节,⽤于存放整数10,其中每个字节都 有地址,上图中4个字节的地址分别是什么呢?
别急,下面我们就学习如何得到a的地址呢?
这⾥就得学习⼀个操作符&-取地址操作符
#include <stdio.h> int main() { int a = 10; &a;//取出a的地址 printf("%p\n", &a); return 0; }
我们从存放a的内存中发现:程序打印出的003EFCEC就是a所占 4个字节中地址较⼩的字节的地址。
1.2 指针变量
那我们通过取地址操作符(&)拿到的地址是⼀个数值,⽐如:003EFCEC ,这个数值有时候也是需要 存储起来,⽅便后期再使⽤的,那我们把这样的地址值存放在哪⾥呢?
答案是: 指针变量中 。
⽐如:
#include <stdio.h> int main() { int a = 10; int* pa = &a;//取出a的地址并存储到指针变量pa中 return 0; }
指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。
正如上面所说,*代表后面的东西时指针变量,int代表指针变量指向对象的类型
那聪明的你,能否猜到:
char ch = 'w';
ch的地址应该存储哪个类型的指针变量呢?
没错!char * p=&ch;
1.3 解引用操作符(*)
上面我们学习到将地址存储到指针变量中,那么通过地址或者指针,我们能否找到地址或者指针指向的对象?答案时可以!
这⾥必须学习⼀个操作符叫解引⽤操作符(*)。
*就是解引⽤操作符, *pa 的意思就是通过pa中存放的地址,找到地址或指针指向的空间,*pa其实就是a变量了;所以*pa = 0,这个操作符是把a改成了0.
1.4 指针变量的大小
在了解指针变量大小之前,我们先了解一下计算机是如何编址的。
⾸先,必须理解,计算机内是有很多的硬件单 元,⽽硬件单元是要互相协同⼯作的。所谓的协
同,⾄少相互之间要能够进⾏数据传递。 但是硬件与硬件之间是互相独⽴的,那么如何通信呢?答案很简单,⽤"线"连起来。
⽽CPU和内存之间也是有⼤量的数据交互的,所以,两者必须也⽤线连起来。
不过,我们今天关心⼀组线,叫做地址总线。
我们可以简单理解,32位机器有32根地址总线, 每根线只有两态,表示0,1【电脉冲有无】,那么
⼀根线,就能表⽰2种含义,2根线就能表⽰4种含义,依次类推。32根地址线,就能表⽰2^32种含
义,每⼀种含义都代表⼀个地址。地址信息被下达给内存,在内存上,就可以找到
该地址对应的数据,将数据在通过数据总线传入CPU内寄存器。
32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,要4 个字节才能存储。
如果指针变量是⽤来存放地址的,那么指针变的⼤⼩就得是4个字节的空间才可以。
同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要 8个字节的空间,指针变量的大小就是8个字节。
下面我们就来验证一下:
#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; }
X86环境输出结果:
X64环境输出结果:
结论:
(1)32位平台下地址是32个bit位,指针变量大小是4个字节。
(2) 64位平台下地址是64个bit位,指针变量大小是8个字节。
(3)注意指针变量的大小和类型是无关关的,只要指针类型的变量,在相同的平台下,大小都是相同的。
1.5 指针变量类型的意义
指针变量的大小和类型无关,只要是指针变量,在同⼀个平台下,大小都是⼀样的,为什么还要有各 种各样的指针类型呢?
其实指针类型是有重要意义!
我们不妨调试一下,下面的代码:
//代码1 #include <stdio.h> int main() { int n = 0x11223344; int* pi = &n; *pi = 0; return 0; }
//代码2 #include <stdio.h> int main() { int n = 0x11223344; char* pc = (char*)&n; *pc = 0; return 0; }
代码1调试结果:
代码2调试结果:
调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第⼀个字节改为0。
结论1:指针的类型决定了,对指针解引⽤的时候有多大的权限(⼀次能操作几个字节)。
比如: char* 的指针解引用就只能访问⼀个字节,而int* 的指针的解引⽤就能访问四个字节。
除此之外,指针变量类型还有其他意义吗?
答案是肯定的,我们不妨分析一下,下面这串代码:
#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; }
代码运行结果如下:
我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。
这就是指针变量的类型差异带来的变化。
结论2:指针的类型决定了指针向前或者向后⾛⼀步有多大(距离)。