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:练习——挑战那些复杂的声明

  • 相关阅读:
    国内大学毕业论文LaTeX模板集合
    LATEX论文排版学习资源汇总
    论文神器Latex30分钟快速入门教程-只需9步向学神看齐
    smartdraw2013破解方法
    科研常用的软件
    推荐科研软件
    斯坦福大学科研软件
    【LaTeX】E喵的LaTeX新手入门教程(6)中文
    【LaTeX】E喵的LaTeX新手入门教程(5)参考文献、文档组织
    【LaTeX】E喵的LaTeX新手入门教程(4)图表
  • 原文地址:https://www.cnblogs.com/52php/p/5683330.html
Copyright © 2011-2022 走看看