指针小解
#include <stdio.h>
#include <stdlib.h>
void
f(void)
{
int a[4];
int *b = malloc(16);
int *c;
int i;
printf("1: a = %p, b = %p, c = %p\n", a, b, c);
c = a;
for (i = 0; i < 4; i++)
a[i] = 100 + i;
c[0] = 200;
printf("2: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
c[1] = 300;
*(c + 2) = 301;
3[c] = 302;
printf("3: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
c = c + 1;
*c = 400;
printf("4: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
c = (int *) ((char *) c + 1);
*c = 500;
printf("5: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
b = (int *) a + 1;
c = (int *) ((char *) a + 1);
printf("6: a = %p, b = %p, c = %p\n", a, b, c);
}
int
main(int ac, char **av)
{
f();
return 0;
}
/*
输出结果如下
1: a = 0x7fff26ed45c0, b = 0x247c010, c = 0x40075d
2: a[0] = 200, a[1] = 101, a[2] = 102, a[3] = 103
3: a[0] = 200, a[1] = 300, a[2] = 301, a[3] = 302
4: a[0] = 200, a[1] = 400, a[2] = 301, a[3] = 302
5: a[0] = 200, a[1] = 128144, a[2] = 256, a[3] = 302
6: a = 0x7fff26ed45c0, b = 0x7fff26ed45c4, c = 0x7fff26ed45c1
*/
首先解释这句,c = (int *) ((char *) c + 1);
这句先将int*类型的c强制转换为char *类型,然后向后移动一位,假如他是int型指针,指向下一个int变量地址,就需要向后移动4字节,即当前地址+4,但此时c是char指针,每个char变量大小为1字节,他的效果就等于c = c + 1 * sizeof(char *); 结果就是c指针向后移动一字节。
再解释下为什么第五次输出a[1]和a[2]变成这两个数
首先如今大多数计算机都采用小端法存储数字,即低位字节存储在高位地址
以十进制 400 和 301 为例,int类型长4B,这两个数16进制存储在内存中是
(内存低地址)00 00 01 90 00 00 01 2d (内存高地址)
但是在小端法中存储的状态是
(内存低地址)90 01 00 00 2d 01 00 00 (内存高地址)
因此当c向后移动一位后,此时他指向的就是01 00 00 2d,转换为正常阅读顺序就是2d 00 00 01
而500的16进制表示为1f4,小端法存储格式是
f4 01 00 00
那么执行*c = 500
后,这段内存就会变为
90 (f4 01 00 00) 01 00 00
那么此时a[1]从90H处开始读取,其结果就是 00 01 f4 90H,转换为10进制就是128144
同理a[2]读出的数是00 00 01 00,即256
小端法有什么好处
网上找了一圈,没有明确答案,比较多人赞同的是:使用小端法存储时,可以使用相同的地址以任意长度读取变量。
比如有一个int变量长度为4B,十进制是400,内存中地址是8
16进制小端法表示是90 01 00 00H
大端法表示是00 00 01 90H
此时我执行强制类型转换,把这个变量变成了一个2B大小的变量,此时他只需读出01 90H
如果用小端法,就仍然可以使用8作为地址,只需读取完01后停下来即可
但是如果用大端法,就必须把地址向后移动2B,变为10才能正常工作。