献给初学指针一头雾水难以理解的朋友们.....
拿到一款RAM只有1KB的51单片机,首先想到的这款MCU的内存1KB=1024个字节,相当于可以存储1024个8bit的数据,假使在未烧入程序的情况,内部1024个字节存储的都是0:
内存编号 | 0 | 1 | 2 | 3 | 4 | 5 | 。。。。 | 1020 | 1021 | 1022 | 1023 |
存的数据 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
现在打开了keil软件,写下第一段代码:
char i=0x08;
编译以后,MCU会自动分配一个内存标号(比如是3,&i=3),表示定义了一个char型变量i,给它赋值0x08,这个0x08存储在内存标号3的小房间里:
内存编号 | 0 | 1 | 2 | 3 | 4 | 5 | 。。。。 | 1020 | 1021 | 1022 | 1023 |
存的数据 | 0 | 0 | 0 | 0x08 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
接着写下第2行代码:
char *p=&i;
编译以后,MCU会自动分配一个内存标号(比如是1020,&p=1020),表示定义了一个指向char型变量的指针变量p,这个指针变量p存储的是i的地址3(内存编号),同时它自己的内存编号是1020,即
内存编号 | 0 | 1 | 2 | 3 | 4 | 5 | 。。。。 | 1020 | 1021 | 1022 | 1023 |
存的数据 | 0 | 0 | 0 | 0x08 | 0 | 0 | 0 | 0x03 | 0 | 0 | 0 |
重点来了!上面的表是错误的!!!!初学者最难理解地方也在这里。
首先要理解这个MCU内存有1024个字节,用十六进制表示从0-1023(0x03 FF),所以每个内存编号(指针)需要2个字节来表示。整型变量i的地址不是简单的3,而是0x00 03,必须用16位来表示,虽然从数值上来说,3和0x0003是相等的,但是地址是16位,不能简单的用十进制数字来表示,而且无论其他32位或64位MCU,每个内存块只能存储一个字节,即8bit的数据,超过8bit的数据,比如long型变量,就需要4个内存块来存储量,这也是初学者最容易忽视的地方@!
p!=0x03,p=0x0003(8051单片机采用大端编码方式,高8位数据0x00在前,低8位数据0x03在后)。所以正确的表格应该是:
内存编号 | 0x0000 | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | ..... | 0x03FC | 0x03FD | 0x03FE | 0x03FF |
存的数据 | 0 | 0 | 0 | 0x08 | 0 | 0 | 0 | 0x00 | 0x03 | 0 | 0 |
这里使用Code_Blocks工具在PC平台上测试一下char型的数据具体是如何存储的,它的指针到底是多少。注意PC平台为小端存储方式,高8位数据在后 。
#include <stdio.h>
typedef unsigned char uint8;
unsigned char test_i=0x08;
unsigned char *test_p=&test_i;
int main()
{
printf("test_i = 0x%x\r\n",test_i);
printf("&test_i = 0x%x\r\n",&test_i);
printf("test_p = 0x%x\r\n",test_p);
printf("test_p+1 = 0x%x\r\n",test_p+1);
printf("*test_p = 0x%x\r\n",*test_p);
printf("&test_p = 0x%x\r\n",&test_p);
printf("&test_p+1 = 0x%x\r\n",&test_p+1);
printf("*(test_p-5 )= 0x%x\r\n",*(test_p-5));
//printf("*(uint8*)0x60fecd= 0x%x\r\n",*(uint8*)0x0060fecd);
printf("*(test_p-4 )= 0x%x\r\n",*(test_p-4));
printf("*(test_p-3 )= 0x%x\r\n",*(test_p-3));
printf("*(test_p-2 )= 0x%x\r\n",*(test_p-2));
printf("*(test_p-1 )= 0x%x\r\n",*(test_p-1));
printf("*(test_p+1 )= 0x%x\r\n",*(test_p+1));
printf("*(test_p+2 )= 0x%x\r\n",*(test_p+2));
printf("*(&test_p+1)= 0x%x\r\n",*(&test_p+1));
}
运行结果:
test_i = 0x08 //test_i的赋值
&test_i = 0x60fed1 //test_i的内存地址,占4个字节
test_p = 0x60fed1 //指针变量test_p存储的值,即test_i的地址
test_p+1 = 0x60fed2 //指针变量test_p存储的test_i地址,因为test_i是char型,一个地址就够了
*test_p = 0x08 //指针变量test_p所指向的地址存储的值,即test_i的赋值
&test_p = 0x60fecc //指针变量test_p自己所存的内存地址,占4个字节
&test_p+1 = 0x60fed0 //指针变量test_p自己所存的内存地址,占4个字节,加1后地址编号+4
*(test_p-5 )= 0xd1 //内存块0x60fecc存储的数据
//*(uint8*)0x60fecd= 0xfe //0x60fece强制转换为uint8型指针,查看内部存的数据
*(test_p-4 )= 0xfe //内存块0x60fecd存储的数据
*(test_p-3 )= 0x60 //内存块0x60fece存储的数据
*(test_p-2 )= 0x00 //内存块0x60fecf存储的数据
*(test_p-1 )= 0x50 //内存块0x60fed0存储的数据
*(test_p+1 )= 0x34 //内存块0x60fed2存储的数据
*(test_p+2 )= 0x12 //内存块0x60fed3存储的数据
*(&test_p+1)= 0x12340850 //指向地址0x60fed0的指针变量存储的数据
内存编号 | 0x60fecb |
0x60fecc &test_p test_p-5 |
0x60fecd test_p-4 |
0x60fece test_p-3 |
0x60fecf test_p-2 |
0x60fed0 &test_p+1 test_p-1 |
0x60fed1 &test_i test_p |
0x60fed2 test_p+1 | 0x60fed3 |
存的数据 | 0 | 0xd1 | 0xfe | 0x60 | 0x00 | 0x50 |
0x08 test_i *test_p | 0x34 | 0x12 |
0x0060fed1 test_p | 0x12340850 *(&test_p+1) |