3.2. 自定义函数

时间 : 16-04-13 栏目 : linux编程 作者 : 老薛 评论 : 0 点击 : 1,623 次

     目前为止我们都在用现有的系统函数,但我们也可以定义自己的函数来用,事实上我们已经这么做了:我们定义了main这个函数。main函数的特殊之处
在于执行程序时它自动被系统调用,系统就认准了“main”这个名字,除了名字特殊之外,main 函数和别的函数没有区别。通过main函数的定义我们已经了解函数定义的语法了: 返回值类型 函数名(参数列表)
{
语句列表
}
其中函数名的命名规则也遵循标识符的命名规则(见第 2.3 节 “变量”),注意自己定义的函数不能跟main函数重名。由于我们定义的main函数不带任何参数,参数列表应写成void,main函数的返回值是int类型的,return 0这个语句就表示返回值是 0,main函数的返回值是返回给操作系统看的,因为main函数是被操作系统调用的,通常程序执行成功就返回 0,在执行过程中出错就返回一个非零值。比如我们将main函数中的return语句改为return 4;再执行它,执行结束后可以在 Shell 中看到它的退出状态:
$ ./hello
$ echo $?
4
$?是 Shell 中的一个特殊变量,表示上一个运行结束的程序的退出状态。关于main函数需要注意两点: 1. [3]书上的main函数定义写成main(){...}形式,不写返回值类型也不写参数列表,这是 Old Style C 的风格。Old Style C 规定不写返回值类型就表示返回int型,不写参数列表就表示参数类型和个数没有明
确指出。这种宽松的规定会导致很多复杂的 Bug 产生,不幸的是现在的C 标准为了兼容旧的代码仍然保留了这种语法,但是读者绝不应该继续使用这种语法。 2. 其实系统在调用main函数时是传参数的,以后会详细解释,所以main函数最标准的形式应该是int main(int argc, char *argv[])。C 标准也规定了int main(void)这种形式,如果不使用系统传进来的两个参数也可以写成这种形式。但除了这两种形式之外,以其它形式定义main函数都是错误的或不可移植的。 关于返回值和return语句我们将在第 5.1 节 “return 语句”详细讨论,我们先从不带参数也没有返回值的函数开始学习定义和使用函数:
例 3.2. 最简单的自定义函数
#include <stdio.h>

void newline(void)

{
printf("\n");
}
int main(void)
{
printf("First Line.\n");
newline();
printf("Second Line.\n");
return 0;
}
执行结果是:
First Line.
Second Line.
我们定义了一个newline函数给main函数调用,它的作用是打印一个换行,所以执行结果中间多了一个空行。newline函数不仅不带参数,也没有返回值,返回值类型为void表示没有返回值[7],这说明我们用这个函数完全是为了利用它的 Side Effect。如果我们想要多次插入空行就可以多次调用newline函数:
int main(void)
{
printf("First Line.\n");
newline();
newline();
newline();
printf("Second Line.\n");
return 0;
}
如果我们总需要三个三个地插入空行,我们可以再定义一个threeline函数每次插入三个空行:
例 3.3. 较简单的自定义函数
#include <stdio.h>
void newline(void)
{
printf("\n");
}
void threeline(void)
{
newline();
newline();
newline();
}
int main(void)
{
printf("Three lines:\n");
threeline();
 printf("Another three lines.\n");  threeline();
return 0;
}
从这个简单的例子中可以体会到:

1. 同一个函数可以被多次调用。
2. 可以用一个函数调用另一个函数,后者再去调第三个函数。
3. 通过自定义函数可以给一组复杂的操作起一个简单的名字,例如
threeline。对于main函数来说,只需要通过threeline这个简单的名字来调用就行了,不必知道打印三个空行具体怎么做,所有的复杂操作都被隐藏在threeline这个名字后面。
4. 使用自定义函数可以使代码更简洁,main函数在任何地方想打印三个空行只需调用一个简单的threeline(),而不必每次都写三个
printf("\n")。
读代码和读文章不一样,按从上到下从左到右的顺序读代码未必是最好的。比如上面的例子,按顺序应该是先看newline再看threeline再看main。如果你换一个角度,按代码的执行顺序来读也许会更好:首先执行的是main函数中的语句,在一条printf之后调用了threeline,这时再去看threeline的定义,其中又调用了newline,这时再去看newline的定义,newline里面有一条printf,执行完成后返回threeline,这里还剩下两次newline调用,效果也都一样,这个执行完之后返回main,接下来又是一条printf和一条threeline。如下图所示:

图 3.1. 函数调用的执行顺序
在这个过程中,我们就在模仿计算机执行这个程序,我们不仅要记住当前读到了哪一行代码,还要记住现在读的代码是被哪个函数调用的,还要记住当前这
段代码返回后应该从上一个函数的什么地方接着往下读。 现在澄清一下函数声明、函数定义、函数原型(Prototype)这几个概念。比如void threeline(void)这一行,声明了一个函数的名字、参数类型和个数、返回值类型,这称为函数原型。在代码中可以单独写一个函数原型,后面加;号结束,而不写函数体,例如: void threeline(void); 这种只能叫函数声明而不能叫函数定义,只有带函数体的声明才叫定义。上一章讲过,只有分配存储空间的变量声明才叫变量定义,其实函数也是一样,编译器只有见到函数定义才会生成指令,而指令在程序运行时当然也是要占存储空间的。那么没有函数体的函数声明有什么用呢?它为编译器提供了有用信息,编译器在处理代码的过程中,只有见到函数原型(不管带不带函数体)之后才知道这个函数的名字、参数类型和返回值,然后在碰到函数调用时才知道怎么生成相应的指令,所以函数原型必须出现在函数调用之前,这也是遵循“先声明后使用”的原则。 在上面的例子中,main调用threeline,threeline再调用newline,要保证每个函数的原型出现在调用之间,就只能按先newline再threeline再main的顺序定义了。如果使用不带函数体的声明,则可以改变函数的定义顺序:
#include <stdio.h>
void newline(void);
void threeline(void);
int main(void)
{
...
}
void newline(void)
{
...
}
void threeline(void)
{
...
}
这样仍然遵循了先声明后使用的原则。 由于有 Old Style C 语法的存在,并不是所有函数声明都包含函数原型,例如声明void threeline();没有明确指出参数类型和个数,所以不算函数原型,这个声明提供给编译器的信息只有函数名和返回值类型。如果在这样的声明之后调用函数,编译器将不做参数类型检查和自动转换,所以很容易引入Bug。读者需要了解这个知识点以便维护旧的代码,但绝不应该按这种风格写代码。 如果在调用函数之前没有声明它会怎么样呢?有的读者也许碰到过这种情况,我可以解释一下,但纯粹是为了满足某些人的好奇心,认真写代码绝不应该这么写的。比如按上面的顺序定义这三个函数,但是把开头的两行声明去掉:
#include <stdio.h>
int main(void)
{
printf("Three lines:\n");
threeline();
 printf("Another three lines.\n");  threeline();
return 0;
}
void newline(void)
{
printf("\n");
}
void threeline(void)
{
newline();
newline();
newline();
}
编译时会报警告:
$ gcc main.c
main.c:17: warning: conflicting types for ‘threeline’
main.c:6: warning: previous implicit declaration of ‘threeline’ was here
但仍然能编译通过,运行结果也对。这里涉及到的规则称为函数的隐式声明(Implicit Declaration),在main函数中调用threeline时并没有声明它,则编译器认为此处隐式声明了int threeline(void);,然后为这个调用生成相应的指令,隐式声明的参数类型和个数根据函数调用代码来确定,隐式声明的返回值类型总是int。然后编译器接着往下看,看到threeline函数的原型是void threeline(void),和先前的隐式声明的返回值类型不符,所以才报这个警告。好在我们也没用到这个函数的返回值,所以执行结果仍然正确。

本文标签

除非注明,文章均为( 老薛 )原创,转载请保留链接: http://www.bdkyr.com/xtyw003/1678.html

3.2. 自定义函数:等您坐沙发呢!

发表评论

4 + 0 = ?


博主微信号,很高兴为您提供帮助

随便看看

为您推荐

0