zoukankan      html  css  js  c++  java
  • 数组与指针

    一、数组的初始化

      1、当初始化列表中的值少于数组元素个数时,编译器会把剩余的元素都初始化为0;

      2、如果初始化数组时省略方括号中的数字,编译器会根据初始化列表中的项数来确定数组的大小;

      3、对于数组中个数的多少,有一个比较不容易出错的方法 - sizeof () 的使用;

      e.g. : int nums[] = {31, 28, 31, 30};

          int count = sizeof nums / sizeof nums[0]; //计算数组中的元素个数

      【sizeof ():给出运算对象的大小,以字节为单位。在上述的语句中,sizeof nums 是整个数组的大小,sizeof nums[0] 是数组中一个元素的大小。】

      4、C99 中可以初始化指定的数组元素。

    int arr[7] = {1, 2, [4] = 23, 8, [1] = 12}

      上述语句的结果为:arr[0] = 1, arr[1] = 12, arr[2] = 0, arr[3] = 0, arr[4] = 23, arr[5] = 8, arr[6] = 0。

       5、C 不允许把数组作为一个单元赋给另外一个数组,除初始化以外也不允许使用花括号列表的形式进行赋值。  

    #define SIZE 5
    int main (void)
    {
        int oxen[SIZE] = {5, 3, 2, 8};
        int yaks[SIZE] ;
        
        yaks = oxen; /*不允许*/
        yaks[SIZE] = oxen[SIZE]; /*数组下标越界*/
        yaks[SIZE] = {5, 3, 2, 8}; /*不起作用*/          
    }   

    二、多维数组的理解 

      1、二维数组的理解

    float rain[5][12];    //内含5个数组元素的数组,每个数组元素内含12个float类型的元素

      换句话说,rain中每个元素本身都是一个内含12个float 类型值的数组。

      2、三维数组的理解

    int box[10][20][30];

      对于box,其理解方式有:1,box内含10个元素,每个元素是内含20个元素的数组,这20个数组元素中的每个元素是内含30个元素的数组;2,把box想像成有10个二维数组(每个二维数组都是20行30列)堆叠起来。

      3、多维数组的理解

      与1、2的内容相类似。

    三、指针 & 数组

      1、指针的值是其指向对象的地址。地址的表示方式依赖于计算机内部的硬件。

      2、指针前使用 ‘ * ’ 运算符可以得到该指针所指向对象的值。

      3、指针加1,指针的值递增其所指向类型的大小(以字节为单位)。

    四、函数 & 数组 & 指针

      1、声明数组形参

      对于函数原型中的数组形参的声明,下面的几种原型是等价的。

    int sum (int *ar, int n);
    int sum (int *, int n);
    int sum (int ar[], int n);
    int sum (int [], int n);

      但是,在函数定义的时候不能省略参数名,下面的几种函数定义是等价的。

    int sum (int *ar, int n){
        //函数主体
    }
    
    int sum (int ar[], int n){
        //函数主体
    }

      2、‘ * ’运算符与‘ ++ ’ / ' -- '运算符的结合

    total += *star++; //先‘递增’后指向
    total += (*star)++; //先指向后递增

      对于上述语句,‘ * ’运算符与‘ ++ ’运算符的优先级相同,但是结合律是从右到左,所以先计算start++,之后再计算*start,先‘递增’后指向。使用后缀形式(start++,而不是++start)意味着先把指针指向位置上的值加到total上,然后再递增指针;如果使用前缀形式(++start,而不是start++),意味着要先递增指针,之后把指针指向位置上的值加到total上。

    五、指针的几种操作

    int urn[5] = { 100, 200, 300, 400, 500 }
    int * ptr1, *ptr2, *ptr3;

      1、赋值

    ptr1 = urn;    //将一个地址赋给指针
    ptr2 = &urn[2];    //将一个地址赋给指针

      2、解引用

    printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p
    ", ptr1, *ptr1, &ptr1);

      解引用 - “ *ptr1”。给出指针指向的地址上存储的值。

      3、取址

    printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p
    ", ptr1, *ptr1, &ptr1);

      取址 - “ &ptr1”。指针也有自己的地址,‘ & ’运算符给出的是指针本身的地址。

      4、指针与整数相加

    ptr3 = ptr1 + 4;    //指针加法

      ' ptr1 + 4 ' 等价于 &urn[4] 。

      5、递增指针

    ptr1++;    //指针递增

      ‘ ptr1++ ’ 等价于ptr1的值加上4(假设系统中 int 数据为4字节),pte1 指向 urn[1] 。

      6、指针减去一个整数

    printf("ptr3 - 2 = %p
    ", ptr3 - 2);

      整数将乘以指针指向类型的大小(以字节为单位),然后用初始地址减去乘积。

      7、递减指针

    ptr2--;    //指针递减

      8、指针求差

    printf("ptr2 - ptr1 = %td
    ", ptr2 - ptr1);

      通常两个指针指向同一个数组的不同元素,通过计算求出两元素之间的距离。差值的单位与数组类型的单位相同。如果 ptr2 - ptr1 = 2,则意味着两个指针所指向的元素之间相差着两个 int ,而并不是两个字节。

      9、比较

      使用关系运算符进行比较,前提是两者都是指向相同类型的对象。

    六、 const 修饰符的使用 

      1、对形参使用 const

    int sum (const int ar[], int n);  /*函数原型*/
    
    int sum (const int ar[], int n){
        //函数体
    }

      上述的 const 修饰符告知编译器:sum 函数不可以修改 ar 指向的数组中的内容。如果在 sum 函数中使用的类似于 ar[i]++的表达式,编译器会很容易地检测到错误。

      这里只用的 const 修饰符并不是要求原数组是常量,而是该函数在处理数组时将其看做是常量,不可更改。

      一般而言,如果函数需要修改数组,在声明数组形参时不使用 const 修饰符;如果编写的函数不用修改数组,在声明数组形参时使用 const 修饰符。

       2、const 修饰符的其他内容

      【 const 数组】

    #define SIZE 3
    ...
    const int data[SIZE] = {2, 4, 7};
    ...
    data[2] = 5;    //编译错误

      【 const 指针】 

    double index[3] = {1.1, 2.3, 4.5};
    const double * pd = index;
    ...
    *pd = 3.8;  //不允许
    pd[2] = 9.0;  //不允许
    index[2] = 9.0; //允许,index 未被限定

      需要注意的是,可以让 pd 指向别处:' pd ++ ;' 是允许的。

      【其他】

      把 const 数据或非 const 数据的地址初始化为指向 const 的指针或为其赋值是合法的。

    double rates [4] = {88.99, 100.12, 4.98, 76.45};
    const double locked[3] = {1.2, 3.5, 7.9};
    const double * pc = rates;    //有效
    pc = locked;    //有效
    pc = &rates[2];    //有效

      只能把非 const 数据的地址赋给普通指针。

    double rates [4] = {88.99, 100.12, 4.98, 76.45};
    const double locked[3] = {1.2, 3.5, 7.9};
    double * pc = rates;    //有效
    pc = locked;    //无效
    pc = &rates[2];    //有效

      C 标准规定,使用非 const 修饰符修改 const 数据,导致的结果是未定义的。【不应该把 const 数组名作为实参传递给相应形参为非 const 数组名的函数。】

       可以声明并初始化一个不能指向别处的指针,关键是 const 修饰符的位置。

    double rates [4] = {88.99, 100.12, 4.98, 76.45};
    double * const pc = rates;    //pc 指向数组的开始
    pc = &rates[1];    //无效,pc 不允许指向别处
    *pc = 99.9;    //有效,更改 rates[0] 的值

      可以使用 const 修饰符两次,使得指针即不能更改其所指向的地址,也不能更改其指向的地址上的值。

    double rates [4] = {88.99, 100.12, 4.98, 76.45};
    const double * const pc = rates;    //pc 指向数组的开始
    pc = &rates[1];    //无效,pc 不允许指向别处
    *pc = 99.9;    //无效,不允许更改 rates[0] 的值

     七、指针与多维数组

      1、简单示例

    int zippo[4][2];
    /*
    zippo  --  二维数组首元素的地址(每个元素都是内含两个 int 类型元素的一维数组)
    zippo + 2 -- 二维数组的第3个元素(即一维数组)的地址
    *(zippo + 2) -- 二维数组的第3个元素(即一维数组)的首元素(一个 int 类型的值)的地址
    *(zippo + 2) + 1 -- 二维数组的第3个元素(即一维数组)的第2个元素(一个 int 类型的值)的地址
    *(*(zippo + 2) + 1) -- 二维数组的第3个一维数组的第2个 int 类型的值,也就是 zippo[2][1]
    */

      2、相关的指针数组

    int (* pz) [2];  //pz指向一个内含两个 int 类型值的数组
    int * pzx[2];  //pax是一个内含两个指针元素的数组,每个元素都指向 int 的指针

      3、指针的兼容性

      【两个不同类型的指针之间不可以相互赋值

      4、需要处理二维数组的函数中的形参的声明

    ...
    #define ROWS 3
    #defien COLS 4
    ...
    void sum_rows (int ar[][COLS], int rows);  //空的方括号表明 ar 是一个指针
    void sum_rows (int (*ar) [COLS], int rows);
    void sum_cols (int [][COLS], int );  //可以,省略形参名
    int sum2d(int (*ar) [COLS], int rows);  //另一种语法

    八、复合字面量【C99 新增

      字面量:除符号常量之外的常量。

      1、示例

      【pre】5 -- int 类型字面量;2.3 -- double 类型字面量;'Y' -- char 类型字面量;"elephant" -- 字符串字面量。

      【新增 - 数组】对于数组,复合字面量类似于数组初始化列表,前面是用括号括起来的类型名。

    double diva [2] = {2, 4};    //普通的数组声明
    (int [2]) {2, 4};    //复合字面量

      【去掉声明中的数组名,留下的 int [2] 即是复合字面量的类型名。】

      2、相关用法

      【与有数组名的数组初始化一样,同样可以省略数组大小,编译器会自动计算数组当前的元素个数。】

    (int []) {10, 20, 40}    //内含3个元素的复合字面量

      【复合字面量是匿名的,必须在创建的同时去使用。常见的用法是指针记录地址。】

    int *pt1;
    pt1 = (int [2]) {10, 20};    //复合字面量的类型名也代表着首元素的地址,*pt1 = 10,pt[1] = 20

      【可以把复合字面量作为实际参数传递给带有匹配形式参数的函数。】

    int sum (const int ar[], int n);
    ...
    int toal3;
    total3 = sum((int []) {4, 4, 4, 5, 5, 5}, 6);//第一个实参:内含6个 int 类型值的数组。把信息传入函数前不必先创建数组,此为复合字面量的典型用法
  • 相关阅读:
    Key ssd_300_vgg/block3_box/L2Normalization/gamma not found in checkpoint的解决方案
    微调(Fine-tune)原理
    TensorFlow的数据读取机制
    卷积神经网络CNN识别MNIST数据集
    TensorFlow基本计算单元与基本操作
    一些小软件闪退的解决方案
    机器学习之SVM调参实例
    机器学习之支持向量机算法(二)
    机器学习之支持向量机算法(一)
    机器学习项目实战----新闻分类任务(二)
  • 原文地址:https://www.cnblogs.com/wyt123/p/11005999.html
Copyright © 2011-2022 走看看