C进阶养成记 - 养成记29:指针和数组分析(下)

--事物的难度远远低于对事物的恐惧!

    上一篇我们已经对指针和数组作了简单的分析,那么这一篇我们继续来对他们做个详细的分析,首先来提出一个问题:

    数组名可以当做常量指针使用,那指针是否也可以当做数组名来使用呢?为了说明这个问题,我们先来看看数组的两种访问方式,对于数组int array[5] = {0};访问方式有:

        -下标形式: array[1] = 2;

        -指针形式:*(array + 1) = 2;

    两种访问方式的比较:

        -指针以固定增量在数组中移动是,效率高于下标访问形式

        -指针增量为1且硬件具有硬件啊增量 模型时,效率更高

        -指针 形式与下标下标形式的转换:

                a[n] -> *(a + n) -> *(n + a) -> n[a]

    虽然说理论上指针形式访问比下标形式效率更高,但是现代编译器的优化率已经相当高,指针与下标两种访问形式效率已经相当了,但是下标访问形式更利于代码的可读性和可维护性。

知道了数组的两种访问方式,我们来看看下边的代码:

#include <stdio.h>

int main()
{
    int a[5] = {0};
    int* p = a;
    int i = 0;
    
    for(i=0; i<5; i++)
    {
        p[i] = i + 1;    //指针去访问
    }
    
    for(i=0; i<5; i++)
    {
        printf("a[%d] = %d\n", i, *(a + i));    //打印数组元素值
    }
    
    printf("\n");
    
    for(i=0; i<5; i++)
    {
        i[a] = i + 10;
    }
    
    for(i=0; i<5; i++)
    {
        printf("p[%d] = %d\n", i, p[i]);
    }
    
    return 0;
}

执行的输出结果为,从输出结果来看,数组确实可以通过指针和下标两种方式去访问:


        

    下边来看一个比较容易出错也比较有意思的知识点:对于一个数组 int array[5],array 和&array有什么区别?

        -array为数组首元素的地址

        -&array为整个数组的地址

        -array 与 &array的值相同,但是代表的意义不同,主要区别在于它们的指针运算

            -array + 1 -> (unsigned int)a + sizeof(*a)

            -&array + 1 -> (unsigned int)(&a) + sizeof(*&a) -> (unsigned int)(&a) + sizeof(a)

            

为了更深的理解上边的内容,下边以一段代码来展示:

#include <stdio.h>

int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    int* p1 = (int*)(&a + 1); 
    int* p2 = (int*)((int)a + 1);
    int* p3 = (int*)(a + 1);
    
    printf("%d, %d, %d\n", p1[-1], p2[0], p3[1]);
    
    return 0;
}
//以上代码,下边那个输出是正确的?
// A. 数组下标不能是负数,程序无法运行
// B. p1[-1]将输出随机数,p2[0]输出2, p3[1]输出3
// C. p1[-1]将输出乱码, p2[0]和p3[1]输出2

下边我们来编译执行一下:


从输出结果来看,好像三个选项都没有符合的,下边我们来逐一分析下:

    - 第一个输出:由于我们是将&a + 1 赋值给p1指针,所以p1指针就指向了数组的末端,那么p[-1] -> *(p-1)是不是就指向前一个数组元素呢?也就是值为5的那个元素,对没错,就是这样的,现在理解了吗?

    - 第二个输出:数组名a代表首元素的地址,但是我们做了(int)a + 1这样的处理,实际就是将首元素的地址转换成普通整数,普通整在加1,就是很普通的数值运算,假如a = 0xaabbccd0,那么(int)a + 1 -> 0xaabbccd1,以这个值作为内存地址去取值,得到的肯定是随机数或乱码。

    - 第三个输出:这个比较容易理解,p3指向数组元素a[1], p3 + 1就指向数组元素a[2],得到的值也就是3。


下边我们来说说数组参数

    -数组作为函数参数时,编译器将其编译成对应的指针

        void fun(int a[]) -> void fun(int *a)

        void fun(int a[5]) -> void fun(int *a)

即数组作为函数参数时,函数内部是不知道数组的大小的,所以一般情况下,函数传入数组参数的同时,需要另外定义一个参数来标示数组大小。


总结:

    -数组名和指针仅仅是可以交互使用,数组名不是指针,指针的本质也不是数组

    -数组名是数组首元素的地址,不是数组的地址

    -函数的数组参数退化为指针

猜你喜欢

转载自blog.csdn.net/lms1008611/article/details/80154543
今日推荐