zoukankan      html  css  js  c++  java
  • 《征服 C 指针》摘录5:函数形参 和 空的下标运算符[]

    一、函数的形参的声明

    C 语言可以像下面这样声明函数的形参:

    void func(int a[])
    {
        // ...
    }

    对于这种写法,无论怎么看都好像要向函数的参数传递数组。

    可是,在 C 中是不能够将数组作为函数的参数进行传递的。无论如何,在这种情况下,你只能传递指向数组初始元素的指针。

    在声明函数形参时,作为类型分类的数组,可以被解读成指针。

    void func(int a[])
    {
    }

    可以被自动地解读为

    void func(int *a)
    {
    }

    此时,就算你定义了数组的元素的个数,也会被无视。

    要点

    只有在声明函数形参的情况下,int a[] 和 int *a 才具有相同的意义。

    下面是一个稍微复杂一点的形参声明的例子:

    void func(int a[][5])

    a 的类型为“int 的数组(元素个数 5)的数组(元素个数不明)”,因此它可以解读成“指向 int 数组(元素个数 5)的指针”。因此,上面的声明本来的意思是:

    void func(int (*a)[5])

    要点

    在下面声明的形参,都具有相同的意义。

    int func(int *a);  /* 写法1 */
    int func(int a[]);  /* 写法2 */
    int func(int a[10]); /* 写法3 */

    写法 2 和 写法 3 是写法 1 的语法糖。

    二、关于空的下标运算符[]

    在 C 语言中,遇到以下情况,下标运算符[]可以将元素个数省略不写。

    对于这些情况,不同编译器会有各自特别的解释,所以不能作为普通的规则来使用。

    (1)、函数形参的声明

    正如上节中说明的那样,对于函数的形参,最外层的数组会被解读成指针,即使定义了元素个数也会被无视。

    (2)、根据初始化表达式可以确定数组大小的情况

    在下面的情况下,编译器可以根据初始化表达式来确定元素的个数,所以可以省略最外层数组的元素个数。

    int a[] = {1, 2, 3, 4, 5};
    char str[] = "abc";
    double matrix[][2] = {{1, 0}, {0, 1}};
    char *color_name[] = {
        "red",
        "green",
        "blue"
    };
    char color_name[][6] = {
        "red",
        "green",
        "blue"
    };

    注意:int a[]; 会报错

    在初始化 数组的数组 的时候,如果有初始化表达式,貌似即使不是最外层的数组,编译器也应该能够确定其元素个数。可是,在 C 语言中,允许下面这样不整齐的数组初始化,因此还是不能简单地确认最外层数组以外的元素个数。

    int a[][3] = { /* int a[3][3]的省略形式 */
        {1, 2, 3},
        {4, 5},
        {6}
    };
    
    char str[][3] = { /* char str[3][5]的省略形式 */
        "hoge",
        "hog",
        "ho"
    };

    似乎可以考虑让编译器选择一个最大值,但 C 的语法并没有这么做。

    如果这么做是为了排查程序员的编程失误,那什么没有把上面“不整齐的数组”也规定为错误?对于这种现象,我至今百思不得其解(莫非只是因为疏忽?)。

    顺便说一下,在初始化上面这样不整齐的数组的时候,没有对应的初始化表达式的元素会被初始化为 0。

    (3)、使用 extern 声明全局变量的情况

    全局变量多个编译单元(.c 文件)中的某一个中定义,然后从其他代码文件通过 extern 进行声明。

    在定义的时候还是需要元素个数的,但是在使用 extern 进行声明的时候,在连接的时候编译器可以确定实际的数组大小,所以可以省略最外层数组的元素个数。

    正如前面说明的那样,只有在声明函数形参的时候,数组的声明才可以被解读成指针。

    像下面这样进行全局变量声明的时候,将数组和指针混在一起,除了程序不能正常运行外,编译器通常也不会报告任何警告或者错误。这一点需要引起注意(如今的链接器,有时也会报错)。

        file_1.c......中
            int a[100];
        file_2.c......中
            extern int *a;

    补充: 声明 和 定义

    在 C 语言中,“声明”在规定变量或者函数的实体的时候被称为“定义”。

    比如,像下面这样声明全局变量的行为,就是“定义”。

    int a;

    以下的 extern 的声明,意味着“使在某处声明的对象能够在当前的地方使用”,因此它不是“定义”。

    extern int a;

    同样地,函数的原型是“声明”,函数的“定义”是指写着函数的实际执行代码的部分。

    自动变量的情况下,区别定义和声明是没有意义的,因为此时声明必然伴随着定义。 

    延伸阅读:

    《征服 C 指针》摘录1:什么是空指针?区分 NULL、0 和 ''

    《征服 C 指针》摘录2:C变量的 作用域 和 生命周期(存储期)

    《征服 C 指针》摘录3:数组 与 指针

    《征服 C 指针》摘录4:函数 与 指针

    《征服 C 指针》摘录5:函数形参 和 空的下标运算符[]

    《征服 C 指针》摘录6:解读 C 的声明

    《征服 C 指针》摘录7:练习——挑战那些复杂的声明

  • 相关阅读:
    Generate Parentheses
    Length of Last Word
    Maximum Subarray
    Count and Say
    二分搜索算法
    Search Insert Position
    Implement strStr()
    Remove Element
    Remove Duplicates from Sorted Array
    Remove Nth Node From End of List
  • 原文地址:https://www.cnblogs.com/52php/p/5683330.html
Copyright © 2011-2022 走看看