关于指针与数组这对兄弟

作者: GLGeek | 来源:发表于2017-06-21 14:26 被阅读162次

    指针和数组是 C 语言中比较重要的知识点,尤其是指针和数组结合之后,很多同学就不清楚是怎么一回事了。在这里结合几个例子,介绍一下我所理解的指针和数组,希望对大家有所帮助。

    指针

    指针在 C 语言中是一种类型,就和其他基本数据类型一样。通常在32位机器上面占四个字节,在64位机器上占八个字节。指针用来保存其他变量的地址,请看下面整型指针与整型变量的对比:

    int main()
    {
        int a = 4;      //定义整型变量 a
        int b = 10;     //定义整形变量 b
        int *p = &b;    //定义整型指针 p ,保存变量 b 的地址
    
        //a 变量和 p 变量的值
        printf("a 变量的值为: %d\n", a);
        printf("p 变量的值为: %p\n", p);
        
        //指针变量的特殊操作
        printf("指针所指向变量的值: %d\n", *p);
        *p = 15;
        printf("指针所指向变量的值: %d\n", *p);
        printf("b 变量的值为: %d\n", b);
        return 0;
    }
    

    解释:
    在上面的程序中,定义了整型变量 a、整型变量 b 和 整型指针变量p,让指针变量 p 保存变量 b 的内存地址,也就是说 p 指针"指向"变量 b 。程序接着打印 a 变量的值和p 变量的值,分别代表整型变量 a 保存的整数值和指针变量 p 保存的变量 b 的地址值(%p表示用十六进制数字表示地址值)。指针还有特殊的操作:*操作,通过*操作可以访问指针所指向的变量,在程序中也就是说 *p 与变量 b 是等价的,操作变量 *p 就是操作变量 b

    类型 变量 特殊操作
    int a a (使用 %d 打印)
    int * p p (使用 %p 打印) *p (代表指向的变量)

    数组

    数组是一种特殊的数据类型,它把具有相同类型的若干变量按照有序的顺序组织起来,极大的方便了我们的开发。数组名代表数组的首地址,即数组第一个元素的地址,通过下标 [] 来访问数组里面的元素。

    int main() 
    {
        int a[5] = {1, 2, 3, 4, 5};
        printf("%d\n", a[0]);
        return 0;
    }
    

    上面代码中的 a[0] 代表数组中的第一个元素,输出的结果为1;

    数组和指针的联系

    其实数组的下标 [] 操作也是一种特殊操作,与指针的 * 操作十分相似。数组名是一个指针:指针常量。它无法改变指向,只能指向数组的首地址,所以a++、a--之类的操作都是错误的(a是数组名)。既然数组名是一个指针,可以使用下标 [] 操作,那么普通的指针变量是不是也可以使用呢?反过来,普通指针的 * 操作是不是也适合数组呢?

    int main()
    {
        int a[4] = {5, 6, 7, 8};
        int *p = a;
        int i;
        printf("输出数组里面的内容:\n");
        for(i = 0; i < 4; i++)
            printf("a[%d] = %d\t", i, a[i]);
        printf("\n");
    
        for(i = 0; i < 4; i++)
            printf("p[%d] = %d\t", i, p[i]);
        printf("\n");
    
        for(i = 0; i < 4; i++)
            printf("*(a+%d)=%d\t", i, *(a+i));
        printf("\n");
    
        for(i = 0; i < 4; i++)
            printf("*(p+%d)=%d\t", i, *(p+i));
        printf("\n");
        return 0;
    }
    

    运行结果:


    运行结果

    从结果可以看出,[]* 操作都适用于指针变量,数组也是通过操作指针来实现对元素的查询、修改操作。让我们再深入一点,数组是如何通过下标操作找到对应的元素的呢?

    int main()
    {
        int a[5] = {1, 2, 3, 4, 5};
        int *p = a;
        printf("%d\n", p[2]);
        printf("%d\n", *(p+2));
    
        p = &a[2];
        printf("%d\n", p[0]);
        printf("%d\n", *p);
        return 0;
    }
    

    运行一下代码发现,输出的数据都是3,也就是数组中的第三个元素。怎么会这样?

    解释:
    程序中当指针 p 指向数组的第三个元素的时候,这时候 p[0] 输出的是3,也就是a[2] 的值,那么 p[0] 的操作结果与 *p 的操作结果一致;另外,大家试一试
    输出 p[1] 的值,不出所料操作结果应与 *(p+1) 的操作结果一致。发现规律没?[] 操作其实就是 * 操作啊!!!发现新大陆了啊,有没有?

    让我们冷静的分析一下: [] 操作和 * 操作实际上都是偏移操作(内存地址偏移),通过当前指针指向的内存地址以及常量来进行地址偏移,最后通过指针的类型解析所指向地址的内容。

    那么偏移的单位是多少?这个与指针或者数组的类型有关,比如 int * 类型的指针一次偏移 sizeof(int) 的地址,double * 类型的指针一次偏移 sizeof(double) 的地址。

    指针访问数组的方式

    1. 使用 [] 访问。例如 int a; int *p = a;用 p 表示输出 a[2] 的值:p[2] ;如果是这样int a[5]; int *p = &a[2];用 p 表示输出a[3] 的值:p[1]

    2. 使用 * 访问。例如int a[5]; int *p = a;用 p 表示输出 a[2] 的值:*(p+2);如果是这样:int a[5]; int *p = &a[2];用 p 表示输出 a[3] 的值:*(p+1)

    思考

    知道指针和数组的操作原理后,下面的题你会做吗?

    第一题:

    int main()
    {
        int a[5] = {1, 2, 3, 4, 5};
        
        p = &a[2];
        printf("%d\n", p[-2]);
        printf("%d\n", *(p-2));
        return 0;
    }
    

    第二题:

    int main()
    {
        int a[5] = {1, 2, 3, 4, 5};
        int *ptr = (int *)(&a + 1);
        printf("%d %d\n", *(a+1), *(ptr-1));
        return 0;
    }
    

    第二题注意指针的类型变化哦,自己编写运行一下,看结果与自己想的一样吗?
    ps: 竟然还能 p[-2],坑爹有没有...

    总结

    1. 指针和数组都可以使用下标或者 * 操作。
    2. 下标操作或者 * 操作的本质都是地址偏移,通过指针的类型来解析内存地址里面存储的数据。
    3. void * 指针可以指向任何地址,但是无法使用 * 操作,原因就是编译器不知道如何解析void * 指针所指向的内存地址的内容。

    相关文章

      网友评论

        本文标题:关于指针与数组这对兄弟

        本文链接:https://www.haomeiwen.com/subject/yzbzqxtx.html