一、前言
在32位系统下,系统给虚拟内存的每个存储单元分配了一个编号,从0x00 00 00 00 开始到 0xff ff ff ff 结束(4个字节),这个编号成为地址(虚拟地址)。而指针就是地址。一个存储单元可以存储一个字节。
指针变量:是一个变量,即用这个变量来存放一个地址编号。因为是在32位平台,地址总线是32位的,所以指针变量占4个字节。
注意:
- 无论什么类型的地址,都是存储单元的编号,也就是虚拟内存,在32位平台下都是4字节。
- 对应类型的指针变量,只能存放对应类型的变量的地址。例如整型的指针变量只能存放整型变量的地址。
如下图,字符ch = 'b’占一个字节,他有一个地址编号,这个地址编号就是ch的地址。整型变量a = 0x12 34 56 78,a占4个字节,就有4个地址编号,默认最小那个为变量的地址,也就是0x00002000。(低位-》高位)
二、指针的定义
关于指针的定义,首先要了解两个符号:
- & :取地址。
- * :定义时表修饰,其他时候表取值。
#include<stdio.h>
int main() {
int *p;//在定义指针变量时*修饰p是一个指针变量
int a = 0x1234abcd;x
p = &a;//用&取地址,把a的地址赋值给p
printf("%x\n",*p);//用*取值,即a,这里输出0x1234abcd
return 0;
}
上面那段代码,p保存了a的地址,也可以说p指向a。
三、指针和变量的关系
通过*p取值,那么有的值是一个字节(char),有的是四个字节(int),而p只存了首地址,那么怎么知道取多少个字节呢,这就取决于指针的类型,如果指针是char类型,那么只会取一个字节,如果指针是int型,那么会再往后取三个字节一共四个字节。
#include<stdio.h>
int main() {
int a = 0x12345678;
char* p1;
p1 = (char*)&a;
printf("%d", *p1);//这里输出是120,也就是英文x
return 0;
}
如上所示,当p1 = (char*)&a;
时(强转了类型),p1保存的是a的首地址,也就是0x78。后面又进行*p1
的取值操作,此时由于p1是char类型,所以只会取一个字节,就导致了最终结果就是0x78,也就是120。
四、指针的自增
首先来看一个例子(注释即使输出结果):
#include<stdio.h>
int main() {
int a = 0x12345678, b = 0xabcdef66;
char* p1;
int* p2;
p1 = (char*)&a;
p2 = &b;
printf("%x\n", p1);//8ff790
printf("%x\n", ++p1);//8ff791
printf("%x\n", p2);//8ff784
printf("%x\n", ++p2);//8ff788
return 0;
}
由此可见,指针的自增不是单纯的指向下一个存储单元这么简单,而是跟指针类型有关,当指针类型为char,每次自增都会+1;而当指针类型为int时,每次自增都会+4。最后总结,指针的自增会直接跳过当前指针所指向变量的所有存储单元,去到下一个存储单元的位置。
五、指针和数组元素的关系
-
直接上图:
指针存放的是数组第一个元素(下标为0)的第一个存储单元的地址。因为数组在虚拟内存的存储中是连续的,因此可以通过指针自增来直接访问到下一个元素。 -
再看一段代码:
#include<stdio.h>
int main() {
int a[] = { 1,2,3,4,5 };
int* p1, * p2;
p1 = a;
p2 = &a[0];
printf("%x\n", p1);//cffeb8
printf("%x\n", p2);//cffeb8
return 0;
}
可以看到数组名就是数组的首地址,因为这是C语言规定的,数组名是一个常量,不能被赋值。
- 对数组元素的引用方法:
#include<stdio.h>
int main() {
int a[] = { 1,2,3,4,5 };
int* p;
p = a;
printf("%d\n", p[2]);//3
printf("%d\n", *(p+2));//3
printf("%d\n", a[2]);//3
printf("%d\n", *(a+2));//3
return 0;
}