对指针的基本认识
指针最基本的用法
int a = 10;
int* p = &a;
指针其实就是存储的地址,现在主流的机器有32位与64位,先来看32位的机器,由于有32个位,因此可以表示2^32字节的地址,因为一个地址小单元就是一个字节,所以表示2^32字节,注意,是字节,因此2^32 Byte =(2^32/2^10)MB = (2^32/2^10/2^10)GB = 2^2 GB, 也就是4G,因此它可以存储4GB的数据,于是,一个地址长度就为4字节,指针变量又是存储的地址,因此在32位系统中,指针变量长度为4字节。
同理可得,在64位操作系统中,指针变量长度为8字节
五个结论
接下来是五个结论,理解这四句话才能打好指针的基础
- 内存被划分为一个个的内存单元,每个内存单元的大小是1个字节
- 每个字节的内存单元都有一个编号,这个编号就是地址,地址在c语言中称为指针
- 地址要存储的话,存放在指针变量中
- 每个内存单元都有一个唯一的地址来标识
- 在32位机器上地址的大小是4个字节,所以指针变量的大小也是4个字节;同理,在64位机器上地址的大小是8个字节,所以指针变量的大小也是8个字节。
指针跳跃
但是,指针存储地址, 地址在同一个机器上位数不变,是不是指针类型的长度就是固定的呢?
请看VCR:
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a = 10;
int *pa = &a;
printf("%lu\n", sizeof((int*)pa));
printf("%lu\n", sizeof((short*)pa));
printf("%lu\n", sizeof((char*)pa));
printf("%lu\n", sizeof((long*)pa));
return 0;
}
运行结果:
那么,每个类型的指针大小都是固定不变的,需要int*, short*这些指针类型干什么呢?是不是他们就没有意义了呢?
其实
指针类型是有意义的
指针类型决定了指针+1 /-1跳过几个字节
客官请看VCR:
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a = 10;
int *pa = &a;
char* pc = &a;
printf("pa = %p\n", pa);
printf("pa = %p\n", pa+1);
printf("pc = %p\n", pc);
printf("pc = %p\n", pc+1);
return 0;
}
运行结果
第一行第三行分别打印的是pa, pc存储的地址, 也就是变量a的地址
可以看到对于int*类型的 pa与pa+1, 他们之间的地址(0x)表示十六进制,地址相差了 e0-dc = 4字节,这也说明pa到下一个地址pa+1跳过了4个字节.
再看pc与pc+1, 他们之间的地址相差了dd-dc = 1字节,这说明pc到下一个地址pc+1跳过了一个字节,刚好是一个字符的大小
因此, 有结论
指针类型是有意义的
指针类型决定了指针+1 /-1跳过几个字节
指针解引用使用
先看指针解引用法最基础的用法:
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a = 10;
int *pa = &a;
*pa = 0;
printf("%d\n", a);
return 0;
}
可以看到, *符号是解引用符号, 可以将指针p存储的地址取出来使用, 这里进行了a的数值的更改
更进一步, 使用指针访问数组
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int *p = arr;
因为, arr是数组名,数组名表示数组首元素的地址等价于int* p = arr[0], 于是,
#include <stdio.h>
int main(int argc, const char * argv[]) {
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int *p = arr;
for (int i=0; i<10; i++) {
*(p+i) = 0;
}
for (int i=0; i<10; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
因为i为指针偏移量, 并且p是int*的指针, 一次跳过四个字节, 也就是一次跳过数组的一个元素, 访问数组下标为i的元素的地址,并将他们都置为0
于是,打印数组可以使用p+i找到下标为i的地址, 并解引用
for (int i=0; i<10; i++) {
printf("%d ", *(p+i));
}
由此有一个冷门但是炫酷的小结论
因为p存储的是arr的地址
因此,可以理解为
p等价于arr
也就是 p+i等价于arr+i
解引用
*(p+i)等价于*(arr+i)都等价于arr[i]
*(arr+i)等价于arr[i]
那么有没有可能 *(i+arr)等价于i[arr]呢?
我们试试
for (int i=0; i<10; i++) {
printf("%d ", i[arr]);
}
!竟然真的可以
为什么呢?
因为编译器在打印或者说访问数组arr[i]时, 会把元素首地址与偏移量取出来, 也就是使用的是*(arr+i), 所以将数组名和下标写反也是没问题滴, 小装逼方法get.