《征服 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 和 '\0'
《征服 C 指针》摘录2:C变量的 作用域 和 生命周期(存储期)
《征服 C 指针》摘录5:函数形参 和 空的下标运算符[]的更多相关文章
- 《征服 C 指针》摘录4:函数 与 指针
一.指向函数的指针 函数名可以在表达式中被解读成“指向函数的指针”,因此,正如代码清单 2-2 的实验那样,写成 func 就可以取得指向函数的指针. “指向函数的指针”本质上也是指针(地址),所以可 ...
- 《征服 C 指针》摘录1:什么是空指针?区分 NULL、0 和 '\0'
一.什么是空指针? 空指针 是一个特殊的指针值. 空指针 是指可以确保没有向任何一个对象的指针.通常使用宏定义 NULL 来表示空指针常量值. 空指针 确保它和任何非空指针进行比较都不会相等,因此经常 ...
- 《征服 C 指针》摘录2:C变量的 作用域 和 生命周期(存储期)
在开发一些小程序的时候,也许我们并不在意作用域的必要性.可是,当你书写几万行,甚至几十万行的代码的时候,没有作用域肯定是不能忍受的. C 语言有如下 3 种作用域. 1.全局变量 在函数之外声明的变量 ...
- 《征服 C 指针》摘录3:数组 与 指针
一.数组 和 指针 的微妙关系 数组 是指将固定个数.相同类型的变量排列起来的对象. 正如之前说明的那样,给指针加 N,指针前进“当前指针指向的变量类型的长度 X N”. 因此,给指向数组的某个元素的 ...
- 《征服 C 指针》摘录6:解读 C 的声明
一.混乱的声明——如何自然地理解 C 的声明? 通常,C 的声明 int hoge; 这样,使用“类型 变量名;”的形式进行书写. 可是,像“指向 int 的指针”类型的变量,却要像下面这样进行声明: ...
- 《征服 C 指针》笔记6:练习——挑战那些复杂的声明
应该是小试牛刀的时候了. 在 ANSI C 的标准库中,有一个 atexit()函数.如果使用这个函数,当程序正常结束的时候,可以回调一个指定的函数. atexit()的原型定义如下: int ate ...
- [C++程序设计]用函数指针变量调用函数
指针变量也可以指向一个函数.一个函数在编译时被分配给一个入口地址.这个函数入口地址就称为函数的指针.可以用一个指针变量指向函数,然后通过该指针变量调用此函数 #include <iostream ...
- 关于strcpy函数形参类型的解析和指针作为输入型输出型参数的不同
在C语言中,字符串一直都是热点,关于strcpy函数大家都很熟悉,但是真正了解的很少,一旦用到总会报一大堆莫名其妙错误,今天我就来给大家详细剖析一下strcpy函数. 虽然不能看到strcpy的内部实 ...
- C++ 函数形参发生动态绑定时指针增长步长与静态类型一致
牛客网上的题: class A { public: long a; }; class B : public A { public: long b; }; void seta1(A* data, int ...
随机推荐
- Oracle约束(Constraint)详解
概述 约束是数据库用来确保数据满足业务规则的手段,不过在真正的企业开发中,除了主键约束这类具有强需求的约束,像外键约束,检查约束更多时候仅仅出现在数据库设计阶段,真实环境却很少应用,更多是放到程序逻辑 ...
- 数据库SQL基本操作
1.查询表结构 desc 表名 2.显示当前连接用户 show user 3.查看系统拥有哪些用户 select * from all_users; 4.查询当前用户下所有对象 select * fr ...
- android第一行代码-6.自定义控件的实现
0.假设一个应用中标题栏控件都是共用的,如果每个activity都需要设置button,绑定方法,那代码就会很臃肿.那我们可以自定义控件,然后继承这个控件就行了. 自定义控件为TitleLayout, ...
- ELK日志系统:Elasticsearch + Logstash + Kibana 搭建教程
环境:OS X 10.10.5 + JDK 1.8 步骤: 一.下载ELK的三大组件 Elasticsearch下载地址: https://www.elastic.co/downloads/elast ...
- image的加载事件(onload)和加载状态(complete)
之前做过这样一个需求,要让商家页的商家图片按照图片外面box的大小等比例缩放.之前的想法是在页面中先输出图片的src,然后在页面底部初始化js,然后在js中写相应的可以使图片按照box的大小等比例缩放 ...
- C#和C实现通过CRC-16 (Modbus)获取CRC值并校验数据(代码)
文章首发于浩瀚先森博客 CRC的全称为Cyclic Redundancy Check,中文名称为循环冗余校验.它是一类重要的线性分组码,编码和解码方法简单,检错和纠错能力强,在通信领域广泛地用于实现差 ...
- JAVA集合介绍
一.集合概述 Java是一种面向对象语言,如果我们要针对多个对象进行操作,就必须对多个对象进行存储.而数组长度固定,不能满足变化的要求.所以,java提供了集合. 特点 1. 长度可以发 ...
- 第二次团队作业——预则立&&他山之石
Deadline: 2016.10.16 22:00pm 一.确立团队选题,建立和初步熟悉团队git的协作方式.项目后续的代码.文档都要通过github增量式管理.实现文档的版本化和增量式管理. 二. ...
- 反序列化问题的研究之java篇
博客园很早就开通了,当时下决心要把自己的经验心得记录上去,但是却没有做到,因为一直觉得自己搞得东西可能还是比较的初级,感觉拿不出手,所以也就是只是把它记录在在印象笔记上面(三年下来,还是整理和收藏了一 ...
- Centos7下安装python,查看python版本
安装Centos的时候,一般会自带默认安装python2.x 一般用python -V可以查看python版本. 我当时安装的时候,运行了那个语句,但是却显示了一大堆出来,虽然里面也带有版本信息,但是 ...