C语言要玩的好,指针是核心,但是指针在学习的时候看教程往往感觉一看就明白,但是实际用的时候一用就错,而且还不知道错在哪。为了更加深刻的了解指针,将学习过程记录下来,依次加深对指针的理解。
先看看一段测试代码
void fun(void)
{
int a = 100; //变量a
int *p1 = &a; //一级指针 存储a的地址
int **p2 = &p1; //二级指针 存储p1的地址
int ***p3 = &p2; //三级指针 存储p2的地址
printf("a = %d,*p1 = %d,**p2 = %d,***p3 = %d\r\n", a, *p1, **p2, ***p3); //显示a的值
printf("&a = %#x,&p1 = %#x,&p2 = %#x,&p3 = %#x", &a, &p1, &p2, &p3);
}
int main(void)
{
delay_init();
LED_Init();
uart_init(115200);
fun();
while(1)
{
LED = 0;
delay_ms(1000);
LED = 1;
delay_ms(1000);
}
}
定义一个变量a,值为100,然后定义一个一级指针p1,p1指向a,然后定义一个二级指针p2,p2指向p1,在定义一个三级指针p3,p3指向p2。
对应关系如下:
然后打印出变量a的值和指针最终对应的值,在打印出变量的地址。通过在keil中单步调试观察这几个变量。
通过串口输出结果为:
由keil中观察变量的值,和输出结果可以看出变量a的地址为 0x20000758,一级指针p1的地址为 0x20000754,二级指针p2的地址为 0x20000750,三级指针p3的地址为 0x2000074c。知道了变量的地址,那么指针的映射关系为:
通过这个对应关系可以很清晰的看出,a中存储的是变量100,p1中存储的是a的地址,p2中存储的是p1的地址,p3中存储的是p2的地址。将这四个变量的值打印出来,看看是不是和分析的一样。
printf("a = %#x,p1 = %#x,p2 = %#x,p3 = %#x\r\n", a, p1, p2, p3);
打印结果为:
a的值为0x64也就是10进制的100,p1的值为a的地址0x20000758,p2的值为p1的地址0x20000754,p3的值为p2的地址0x20000750。由此可以看出除了变量a中存储的是值100外,不管是几级指针,它里面存储的都是它所指向变量的地址。如果要知道指针所指向的是哪个变量,只要打印出指针变量的值,这个值就是它所指向变量的地址。那么如果要知道指针所指向变量中存的值,那么就要用到取值运算符*,在指针前面加一个 * 星号,就代表要取这个指针变量中所存储地址中的变量。现在给指针变量前加一个"*"星号,打印出来看来结果。
printf("a = %#x,*p1 = %#x,*p2 = %#x,*p3 = %#x\r\n", a, *p1, *p2, *p3);
可以看出 *p1 的值 就是变量a的值100, *p2的值为a的地址 0x20000758, *p3的值为p1的地址0x20000754。
对于多级指针来说,加一个星号,就会向前查找一级地址中所对应的值。
这次给指针变量前加两个星号打印看看。
printf("a = %#x,**p1 = %#x,**p2 = %#x,**p3 = %#x\r\n", a, *p1, **p2, **p3);
给二级指针p2前面加两个星号,打印出来就是变量a的值,给三级指针前面加两个星号,打印出来就是变量a的地址。也就是说指针会向前查找两级地址中所存储的变量值。
下来给指针前面加三个星号。
printf("a = %#x,**p1 = %#x,**p2 = %#x,***p3 = %#x\r\n", a, *p1, **p2, ***p3);
加上三个星号后,打印出来的是变量a的值100。说明加上三个星号后,指针会向前查找三级地址中所存储的变量值。
通过上面实验可以更深刻的了解指针,指针变量内存储的是一个地址,如果要获取这个地址就直接调用指针变量如:p1;如果要获取这个地址中存贮的值,就要用取值运算符 “*” ,如: *p;如果要获取这个指针变量自己本身的地址,就要用取地址运算符 “&” ,如:&p1。
指针和普通变量的区别是,普通变量中存储的数字代表一个值,而指针变量中存储的数字是一个地址。可以理解为,普通变量中的数字就是 快递包裹中的东西,而指针变量中的数字就是快递包裹要送去的地址。使用指针时就要清楚的知道自己想要的是是快递包裹的地址还是想要包裹中的东西。