一、函数的形参的声明

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 和 '\0'

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

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

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

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

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

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

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

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

    一.指向函数的指针 函数名可以在表达式中被解读成“指向函数的指针”,因此,正如代码清单 2-2 的实验那样,写成 func 就可以取得指向函数的指针. “指向函数的指针”本质上也是指针(地址),所以可 ...

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

    一.什么是空指针? 空指针 是一个特殊的指针值. 空指针 是指可以确保没有向任何一个对象的指针.通常使用宏定义 NULL 来表示空指针常量值. 空指针 确保它和任何非空指针进行比较都不会相等,因此经常 ...

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

    在开发一些小程序的时候,也许我们并不在意作用域的必要性.可是,当你书写几万行,甚至几十万行的代码的时候,没有作用域肯定是不能忍受的. C 语言有如下 3 种作用域. 1.全局变量 在函数之外声明的变量 ...

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

    一.数组 和 指针 的微妙关系 数组 是指将固定个数.相同类型的变量排列起来的对象. 正如之前说明的那样,给指针加 N,指针前进“当前指针指向的变量类型的长度 X N”. 因此,给指向数组的某个元素的 ...

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

    一.混乱的声明——如何自然地理解 C 的声明? 通常,C 的声明 int hoge; 这样,使用“类型 变量名;”的形式进行书写. 可是,像“指向 int 的指针”类型的变量,却要像下面这样进行声明: ...

  6. 《征服 C 指针》笔记6:练习——挑战那些复杂的声明

    应该是小试牛刀的时候了. 在 ANSI C 的标准库中,有一个 atexit()函数.如果使用这个函数,当程序正常结束的时候,可以回调一个指定的函数. atexit()的原型定义如下: int ate ...

  7. [C++程序设计]用函数指针变量调用函数

    指针变量也可以指向一个函数.一个函数在编译时被分配给一个入口地址.这个函数入口地址就称为函数的指针.可以用一个指针变量指向函数,然后通过该指针变量调用此函数 #include <iostream ...

  8. 关于strcpy函数形参类型的解析和指针作为输入型输出型参数的不同

    在C语言中,字符串一直都是热点,关于strcpy函数大家都很熟悉,但是真正了解的很少,一旦用到总会报一大堆莫名其妙错误,今天我就来给大家详细剖析一下strcpy函数. 虽然不能看到strcpy的内部实 ...

  9. C++ 函数形参发生动态绑定时指针增长步长与静态类型一致

    牛客网上的题: class A { public: long a; }; class B : public A { public: long b; }; void seta1(A* data, int ...

随机推荐

  1. 2016.10.30 NOIP模拟赛 day2 PM 整理

    满分:300分 直接全部爆零,真的是很坑啊! 10.30的题目+数据:链接:http://pan.baidu.com/s/1jHXLace 密码:i784 T1: 题目中的难点就是每次折叠的点可能应经 ...

  2. NYOJ 485

    A*B Problem 描述 设计一个程序求出A*B,然后将其结果每一位相加得到C,如果C的位数大于等于2,继续将C的各位数相加,直到结果是个一位数k. 例如: 6*8=48: 4+8=12: 1+2 ...

  3. Struts2 easy UI插件

    一.easy UI是类似于jQuery UI的插件库,它提供了丰富的各种常用插件:tree.datagrid... tree插件: 语法:$(selector).tree([settings]); 常 ...

  4. [No00007C]dreamweaver cc 注释快捷键

    设置快捷键入口:编辑->快捷键 打开后: 先复制一份快捷键拷贝(系统自带的不让修改), 选择代码编辑 下拉到后面,设置快捷键

  5. SQL笔记

    1.增加.删除约束 ALTER TABLE 表名 ADD CONSTRAINT 约束名 UNIQUE(列1名,列名2) ALTER TABLE 表名 DROP CONSTRAINT 约束名 2.查询更 ...

  6. vijos P1915 解方程 加强版

    背景 B酱为NOIP 2014出了一道有趣的题目, 可是在NOIP现场, B酱发现数据规模给错了, 他很伤心, 哭得很可怜..... 为了安慰可怜的B酱, vijos刻意挂出来了真实的题目! 描述 已 ...

  7. asp.net(C#)网站发布后 Global.asax 里 Application_Error 不执行的问题

    现象 在 Global.asax 用 Application_Error 捕捉了http的404,500等错误,在本机测试正常,发布后无效,几经周折终于解决了... 程序是这样设计的 Applicat ...

  8. jeecg小吐槽续——自己折腾修改在线开发功能中“默认值”的使用

    -- 原来设置了"默认值"的字段,新建表单时不会出现在表单上,要保存后才能在列表页面出现,而且第二次编辑时,设置了"默认值"的字段再也不能改成空值! -- 要修 ...

  9. python 中文乱码问题2

    1.文件存为utf-8的格式,windows和linux两个环境都可以用 2.中文输出可以采用u方法 比如:print u'成年'

  10. springMVC文件上传

    参考的地址:http://www.tuicool.com/articles/nMVjaiF 1.需要使用的jar. commons-fileupload.jar与commons-io-1.4.jar二 ...