指针其实也是一个变量,只是这个变量比较特殊,专门存放其他变量在内存上的位置值(叫做地址值)。
就像一本书上的目录,它不包含每篇文章的内容,但它携带了对应文章的页码,比如我们要查看哪一篇文章在哪一页,去看一下目录找到对应的文章标题,它的末尾就写明了这篇文章在哪一页,然后再翻到那一页,我们就能读取文章内容了。
我们定义一个指针,让它指向一个变量,再把他打印出来:
1 2 3 4 5 6 7 8 9 10 11 |
#include <stdio.h> int main(int argc, char **argv) { int num = 26; int *p = # printf("*p = %d\n", *p); return 0; } |
运行结果:
我们来详细讲解一下,指针是怎么运作的。
1 2 3 |
int num = 26; |
首先我们定义了一个int型的变量 num, 并赋初值26. 代码运行后便会在内存的某个地址中初始化这个变量,假设该地址在0x1a处。如下图:
当我们执行如下操作:
1 2 3 |
int *p = # |
&num会获得num在内存中的地址0x1a, 再赋值给指针变量p,之后p=0x1a,假设p变量被放在内存中的0x20处,那么指针变量的示意图可以表示为如下:
再执行printf打印*p时, 实际的操作过程是取得p的值(0x1a), 再去找到内存0x1a处,获取里面的数据26,再调用printf打印出这个值。
那么如果我们只打印p会得到什么呢?
我们继续看如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <stdio.h> int main(int argc, char **argv) { int num = 26; int *p = # printf("The address of num: %p\n", &num); printf("p = %d\n", p); printf("p = %p\n", p); return 0; } |
运行结果(注意每次运行的结果可能不同,因为开辟的内存地址可能不同):
其中%p表示目标以地址的形式输出。
我们可以看到,p直接以%d的格式输出的话,值为一个负数-818621124
,那么它与0x7ffecf34d53c有什么关系呢?
其实,-818621124是0x7ffecf34d53c的二进制的低32位转化成10进制后的值,-818621124在计算机中是以补码的形式存在的,其为负数,表示最高位(符号位,正号为0,负号为1)为1。
因为 源码的补码=源码的反码+1;
所谓反码就是在二进制的情况下,除了符号位,对每一位取反。
知道了上面的规律,我们就可以反推出-818621124在计算机中存储时候的二进制情况了。
先计算一下-818621124的二进制表示形式:
1 2 3 |
1011 0000 1100 1011 0010 1010 1100 0100 (这里的最高位代表负号) |
1 2 3 |
得出反码为:1100 1111 0011 0100 1101 0101 0011 1011 |
1 2 3 |
得出补码:1100 1111 0011 0100 1101 0101 0011 1100 |
再看一下0x7ffecf34d53c的二进制形式:
1 2 3 |
111 1111 1111 1110 1100 1111 0011 0100 1101 0101 0011 1100 |
对比-818621124的补码和0x7ffecf34d53c的二进制有没有发现什么?
没错-818621124的32位二进制补码和0x7ffecf34d53c的低32位完全相同。
但指针是8字节的,8*8=64位,那么问题又来了,为什么是指针值的低32位才相同,而不是64位呢?
那是因为int型是4字节,即4*8=32位,而我们打印出p的时候,用的是%d对应的就是int型,所以它才会把p的值的低32位转化成10进制给打印出来,当然就不是取64位转化打印出来了。
END~