C语言学习笔记---指针和数组

数组是相同类型的数据集合,会在内存中占用连续的一块内存。而指针是存储的一个地址,在内存中不会占用连续的内存。
先来写一段测试代码

void fun(void)
{
   int i;
   int num[10]={0,1,2,3,4,5,6,7,8,9};
   int *p;
   p = num;
   for(i=0;i<10;i++)
   {
	 printf("%d ",num[i]);
   }
   printf("\r\n------------------------------\r\n");
   
    for(i=0;i<10;i++)
   {
	 printf("%d ",p[i]);
   }
   printf("\r\n");
}

int main(void)
{
    delay_init();
    LED_Init();
    uart_init(115200);
    fun();
    while(1)
    {
        LED = 0;
        delay_ms(1000);
        LED = 1;
        delay_ms(1000);
    }
}

定义一个整形数组,然后定义一个指针,将数组首地址传给指针,然后用数组和指针分别打印内容。打印结果为:
在这里插入图片描述
在keil的单步模式下观察指针和数组
在这里插入图片描述
通过变量观察可以看出,指针p内存储的值就是数组num的首地址0x2000072C。
说明将数组传给指针时,并没有将数组全部传递给指针,只是将数组的首地址传递给了指针。
下来打印出数组中每个变量的地址,然后通过指针打印数组,观察指针内存储地址的变化情况 。

    for(i = 0; i < 10; i++)
    {
        printf("value: %d ===> addr: %#x\r\n", num[i], &num[i]);
    }
    printf("\r\n------------------------------\r\n");

    for(i = 0; i < 10; i++)
    {
        printf("value: %d ---->addr:%#x\r\n", *p, p);
        p++;
    }
    printf("\r\n");

在这里插入图片描述
单步调试观察指针打印情况,当打印函数调用*p时,输出的是数组中的值,当执行p++时,p中的内容加4,也就是p中所存的地址加1,由于数组是int,而一个int占4个字节,所以地址加1时,数值就会加4。串口输出情况如下:
在这里插入图片描述
可以看出通过指针操作数组时,指针变量p内永远存储的只有一个地址。当直接操作p时,p内存储的地址就会发生变化,而通过 *p 指令可以读出当前地址中所存储的内容。所以在用指针操作数组时,要特别注意下标的控制。假如用指针打印15组数据会发生什么情况?

 for(i = 0; i < 15; i++)
    {
        printf("value: %d ---->addr:%#x\r\n", *p, p);
        p++;
    }

在这里插入图片描述
可以看出当循环超过10次的时候,指针的地址每次还是增加一个int字节的长度,但是此时地址中所存储的数据已经乱了,所以在用指针操作数组的时候,要时刻注意指针 p 中的地址所指向的数据有没有超出数组范围。否则可能就会出现程序异常。
下来再看看指针不同的操作方法有什么区别,比如: p++、*p++、 *(p++)、(*p)++有什么区别。

    printf(" ------------  p++ ------------ \r\n");
    p = num;
    printf("value: %d ---->addr:%#x\r\n", *p, p);
    p++;  
    printf("value: %d ---->addr:%#x\r\n", *p, p);

    printf(" ------------  *p++ ------------ \r\n");
    p = num;
	printf("value: %d ---->addr:%#x\r\n", *p, p);
	*p++;   
    printf("value: %d ---->addr:%#x\r\n", *p, p);

    printf(" ------------  *(p++) ------------ \r\n");
    p = num;
	printf("value: %d ---->addr:%#x\r\n", *p, p);
    *(p++);  
    printf("value: %d ---->addr:%#x\r\n", *p, p);
	
	 printf(" ------------  (*p)++ ------------ \r\n");
    p = num;
	printf("value: %d ---->addr:%#x\r\n", *p, p);
    (*p)++;  
    printf("value: %d ---->addr:%#x\r\n", *p, p);

把数组首地址给指针p,然后通过不同的操作方式来操作指针,看看输出结果有什么区别。
在这里插入图片描述
通过结果可以看出前三种操作方法 p++、*p++、 *(p++)输出结果都一样,都是指针的地址增加了一个int字节,同时输出的值为1。
而最后一种操作方法(*p)++,输出的值为1,但是指针内存储的址没变。
说明前3中方法中执行++操作的时候,操作的都是指针p中所存的地址值。而最后一种方法执行++操作时,操作的是指针p内所存地址指向的值。
来看看最后一种方法和其他的区别(*p)++,先执行(*p),此时p内所存的地址是0x20000730, *p也就是取0x20000730地址中存储的数据值,也就是0,所以(*p)执行完的结果是0,然后执行0++,也就是将数组第0个元素值加1,0+1的结果为1。此时指针p内所指向的地址依然是数组中第0个元素的地址。
下来将++放在指针p前面看看会发生什么情况,同样测试4种方式 ++p、++ *p、
*(++)p、++(*p)。

printf(" ------------  ++p ------------ \r\n");
    p = num;
    printf("value: %d ---->addr:%#x\r\n", *p, p);
    ++p;  
    printf("value: %d ---->addr:%#x\r\n", *p, p);

    printf(" ------------  ++*p ------------ \r\n");
    p = num;
	printf("value: %d ---->addr:%#x\r\n", *p, p);
	++*p;   
    printf("value: %d ---->addr:%#x\r\n", *p, p);

    printf(" ------------  *(++p) ------------ \r\n");
    p = num;
	printf("value: %d ---->addr:%#x\r\n", *p, p);
    *(++p);  
    printf("value: %d ---->addr:%#x\r\n", *p, p);
	
	 printf(" ------------  ++(*p) ------------ \r\n");
    p = num;
	printf("value: %d ---->addr:%#x\r\n", *p, p);
    ++(*p);  
    printf("value: %d ---->addr:%#x\r\n", *p, p);

运行结果如下
在这里插入图片描述
++p执行前,p中存储的是数组中第0个元素的地址,执行完 ++p之后,p中存储的是数组中第1个元素的地址。

++*p执行前,p中存储的是数组中第0个元素的地址,执行完 ++*p之后,p中存储的依然是数组中第0个元素的地址。说明++*p执行时 先执行的是 *p ,取出第0个元素的值,然后执行++运算,对值进行加1操作。而没有操作地址。

*(++p) 执行前,p中存储的是数组中第0个元素的地址,执行完 *( ++p)之后,p中存储的是数组中第1个元素的地址。但是发现一个问题, *(++p)执行前输出的数组第0个元素为什么是1?而不是0?这时因为在执行上面++*p的代码时,++*p已经将数组中第0个元素的值由0改为了1。

++(*p)执行前,p中存储的是数组中第0个元素的地址,执行完 ++(*p)之后,p中存储的依然是数组中第0个元素的地址。说明++(*p)执行时 先执行的是 *p ,取出第0个元素的值,然后执行++运算,对值进行加1操作。而没有操作地址。而第0个元素的值已经在上面执行的过程中被变为了1,所以执行++运算后,值变为了2 。

执行完上面操作后,此时数组中的值为:
在这里插入图片描述
数组中第0个元素的值由0变为了2,其他位置的值都未发生改变。通过上面的实验可以更清楚的了解,用指针操作数组时,指针在程序内部时如何运算的。
下面用指针对数组进行排序

void fun(int *p, int len)
{
    int i = 0, j = 0, tem = 0;
    for(i = 0; i < len - 1; i++)
    {
        for(j = 0; j < len - 1 - i; j++)
        {
            if(*(p + j) < * (p + j + 1))
            {
                tem = *(p + j);
                *(p + j) = *(p + j + 1);
                *(p + j + 1) = tem;
            }
        }
    }
}

int main(void)
{
    int i = 0;
    int num[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    delay_init();
    LED_Init();
    uart_init(115200);

    printf("排序前\r\n");
    for(i = 0; i < 10; i++)
    {
        printf("%d ", num[i]);
    }

    fun(num, 10);
    printf("\r\n排序后\r\n");
    for(i = 0; i < 10; i++)
    {
        printf("%d ", num[i]);
    }
    while(1)
    {
        LED = 0;
        delay_ms(1000);
        LED = 1;
        delay_ms(1000);
    }
}

输出结果为
在这里插入图片描述
在用指针操作数据时,一定要先改变指针所指向的地址,然后再取当前地址对应的值如 *( p + j) ,p指向的是数组首地址,也就是第0个元素的地址 p + j 代表将地址指向数组中第 j 个元素的地址,然后在用 * 号取改地址中对应的值。如果写成了
*p + j,那么就成了 取第0个元素地址对应的值,在给元素的值加 j 。 如果感觉这样操作指针比较麻烦,那么也可以将 *(p + j),写成 p[ j ]。将指针当着数组一样使用就行。
那么代码就可以改为

void fun(int *p, int len)
{
    int i = 0, j = 0, tem = 0;
    for(i = 0; i < len - 1; i++)
    {
        for(j = 0; j < len - 1 - i; j++)
        {
            if(p[j] < p[j+1])
            {
                tem = p[j];
                p[j] = p[j+1];
                p[j+1] = tem;
            }
        }
    }
}

那为什么 * (p + j) 不写成 * p[ j ],那是因为在此处[ ]的意义不是方括号,而代表的是下标运算。具体的表现形式是 “地址+[ ]” ,由于p已经代表的是数组的首地址,那么 p[ j ] 就意味着取 数组中j下标的值,类似于数组 num[ j ]的作用一样,num代表的是数组首地址,num[ j ] 就是首地址偏移 j 个地址处存储的数据。如果此时在加上 * ,就意味着将 p[ j ] 这个地址存储的值又当做地址,在取此地址上存储的数据。例如 p[5] 的结果为5,那么 * p[5]的结果就是取 0x00000005 这个地址上对应的数据,这样的话程序运行就会出错。
由此可见数组 num[i] 也就可以写成 *(num + i) 。
那么可以打印数组的时候代码就可以改为

 printf("排序前\r\n");
    for(i = 0; i < 10; i++)
    {
        printf("%d ",  num[i]);
    }

    fun(num, 10);
    printf("\r\n排序后\r\n");
    for(i = 0; i < 10; i++)
    {
        printf("%d ",*(num + i));
    }

打印结果为
在这里插入图片描述
这样看来数组也相当于指针,不过它表面上看起来没有指针操作的标志 " * "号,但实际上C语言编译的时候还是把数组当做指针来操作的。
所以在程序中对数组的操作属于地址引用而不是值引用。

发布了76 篇原创文章 · 获赞 30 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_20222919/article/details/100770833