• 数组与指针


    1.定义

      指针:在K&R C上的原文为“A pointer is a group of cell (often two or four) that can hold an address.” 从概念上看,指针是包含地址的变量。
      数组:能存储连续固定数量的相同类型元素的数据结构。

    2.指针数组和数组指针

    int *arr_for_p[5];      // 指针数组
    int (*p_for_arr)[5];    // 数组指针

    由于[]的优先级高于*,所以第一条语句可以写成这样

    int* (arr_for_p[5]);

    而第二条语句的解释可以理解为(*p_for_arr)代表了一个5个整数的数组,而p_for_arr就是这个数组的指针,也就是这个数组首地址,所以使用起来类似于(*p_for_arr)[i]。实际上p_for_arr和*p_for_arr是同一个值,因为指针变量指向的是数组的首地址,而指针变量取消引用之后得到的是这个数组(实质上也是数组的首地址),所以它们的值是一样的。

    3.数组首地址与数组首元素的首地址

    #include <stdio.h>
    
    int main(void) {
        int a[5];
    
        int (*ptr_1)[5] = a;
        printf("ptr_1 = %p(%u)
    ", ptr_1, ptr_1);
        int (*ptr_2)[5] = &a;
        printf("ptr_2 = %p(%u)
    ", ptr_2, ptr_2);
    
        return 0;
    }
    
    /*      
            结果:
            ptr_1 = 0xbfc9a144(3217662276)
            ptr_2 = 0xbfc9a144(3217662276)
     */

    从C的层面上理解a是数组首元素的首地址,而&a是数组的地址,很显然在栈上分配的数组是一段连续的内存,这里是20个字节,那么可以知道a和&a的值就是一样的——都是这段内存的起始地址,去掉打印语句我们得到的汇编代码如下

    main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $32, %esp
        leal    -28(%ebp), %eax    # a
        movl    %eax, -8(%ebp)
        leal    -28(%ebp), %eax    # &a
        movl    %eax, -4(%ebp)
        movl    $0, %eax
        leave
        ret

    在汇编层面上它们就是相同的值,都是%ebp-28。

    4.数组指针运算

    #include <stdio.h>
    
    int main(void) {
        int a[5];
        int (*ptr_a)[3] = a;
        int (*ptr_b)[5] = a;
        int (*ptr_c)[10] = a;
    
        unsigned long tmp = a;
        printf("tmp = %p(%d)
    ", tmp, tmp);
        tmp = ptr_a+1;
        printf("tmp = %p(%d)
    ", tmp, tmp);
        tmp = ptr_b+1;
        printf("tmp = %p(%d)
    ", tmp, tmp);
        tmp = ptr_c+1;
        printf("tmp = %p(%d)
    ", tmp, tmp);
    
        return 0;
    }
    
    /*
        结果:
        tmp = 0020FAA8(2161320)
        tmp = 0020FAB4(2161332)
        tmp = 0020FABC(2161340) 
        tmp = 0020FAD0(2161360)
     */

    可以看到三个数组的指针加1后的偏移都不同,分别是12,20和40,所以它们是以数组为单位偏移,而这些都是编译器的工作。

    5.多维数组

    本质上多维数组和一维数组并无二致,只是进行多次下标运算而已,看下面的代码

    int arrs[10][10];
    
    printf("arrs = %p
    ", arrs);
    printf("arrs[0] = %p
    ", arrs[0]);
    printf("&arrs[0][0] = %p
    ", &arrs[0][0]);
    
    /*
     * 在Visual Studio 2010 中显示结果为:
     * arrs = 0031FBB4
     * arrs[0] = 0031FBB4
     * &arrs[0][0] = 0031FBB4
     */

    6.多级指针

    多级指针本质上也是指针,也就是含有地址的变量,关键是这个地址要合法。

    7.多级指针与多维数组

    我个人认为这两者没有什么必然的联系,除非动态分配多维数组,不过始终记住多维数组下标运算编译器采用的是首地址偏移的方式,而多级指针下标运算采用的是指针多次解除引用的方式,二者完全不同。请看下面的代码

    int main(void) {
            int arrs[3][4];
            int** pparrs = (int **)arrs;
            
            arrs[2][2] = 33;
            pparrs[2][2] = 33;
            
            return 0;
    }

    汇编代码如下

    main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $64, %esp
        leal    -52(%ebp), %eax  # 下面是直接偏移寻址
        movl    %eax, -4(%ebp)
        movl    $33, -12(%ebp)
        movl    -4(%ebp), %eax  # 下面是取消多级引用
        addl    $8, %eax
        movl    (%eax), %eax
        addl    $8, %eax
        movl    $33, (%eax)
        movl    $0, %eax
        leave
        ret

    当然上面的代码直接运行肯定会宕掉。

  • 相关阅读:
    华硕路由器5g/2.4g信道合并
    反冲(倒冲)
    成本中心与利润中心
    IFRS
    word首行大写关闭
    电子笔记还是手写笔记
    SAP程序跟踪
    Python银行机器学习:回归、随机森林、KNN近邻、决策树、高斯朴素贝叶斯、支持向量机svm分析营销活动数据
    R语言结构方程模型SEM分析心理学和营销研究数据路径图可视化
    拓端tecdat|Python信贷风控模型:梯度提升Adaboost,XGBoost,SGD, GBOOST, SVC,随机森林, KNN预测金融信贷违约支付和模型优化
  • 原文地址:https://www.cnblogs.com/wendellyi/p/3245884.html
Copyright © 2020-2023  润新知