本文介绍: 函数将大的计算任务拆分成小份,还能让我们在他人已完成的基础上开发,而不必从头开始。恰当的函数隐藏程序中那些不需要知道操作细节部分,这样就使整体更加清晰,并且能减少修改痛苦。C的设计使得函数使用高效而简单;C程序通常由很多函数,而不是由几个大函数构成。一个程序可以放在一个多个源文件中。多个源文件可以分开编译,并与库中早已编译好的函数一起加载。然而我们这里不会介绍这个过程,因为各个系统细节都不一样。函数声明定义是ANSI标准对C语言做出最显著改动的地方。

第4章 函数

函数将大的计算任务拆分成小份,还能让我们在他人已完成的基础上开发,而不必从头开始。恰当的函数隐藏程序中那些不需要知道操作细节的部分,这样就使整体更加清晰,并且能减少修改痛苦

C的设计使得函数的使用高效而简单;C程序通常由很多小函数,而不是由几个大函数构成。一个程序可以放在一个多个源文件中。多个源文件可以分开编译,并与库中早已编译好的函数一起加载。然而我们这里不会介绍这个过程,因为各个系统的细节都不一样。

函数声明定义是ANSI标准对C语言做出最显著改动的地方。如我们最早在第一章所见,当函数声明可以声明参数类型。函数定义语法也改了,这样声明定义匹配了。这使编译器可能比之前检测到更多的错误。更进一步,当参数正确声明后,会自动执行合适的强制类型转换

标准澄清了名称范围规则;还特别要求每个外部对象只能有一个定义初始化更加通用:可以初始化自动数组结构

C预处理也得到加强。新的预处理机制包含一套完整条件编译指令集合,从宏参数创建字符串方式,以及对宏扩展过程更好控制

4.1函数基础

首先,让我们设计编写一个程序,它将打印包含某个特定“样式”或字符串输入行。(这是Unix程序 grep一个特例。)例如,在下列行中搜索包含ould” 这几个字母样式

Ah Love! could you and I with Fate conspire

To grasp this sorry Scheme of Things entire,

Would not we shatter it to bitsand then

Re-mould it nearer to the Hearts Desire!

会得到

Ah Love! could you and I with Fate conspire

Would not we shatter it to bitsand then

Re-mould it nearer to the Hearts Desire!

这项工作可以很整齐地分为部分

while (there is another line)

        if (the line contains the patten)

                print it

尽管可以将所有这些代码放到 main 函数里,但更好方式是把每个部分都做成独立的函数,以便合理利用这个结构。三个小块比一个大块好处理,因为无关的细节可以隐藏到函数里,而且把多余交互的机会最小化了。另外这些小块甚至可能用到其他程序中。

while (there is another line) ”就是 getline,我们第一章写的函数。而 “print it ” 就是 printf ,别人早已经提供给我们了。这意味着我们只需要一个例程确定该行是否包含了样式

解决这个问题,可以写一个函数 strindex(s, t) 返回字符串 t 在字符串 s 中的起始位置索引,如果 s 不包含 t 则返回 -1。因为 C 的数组位置从0开始,索引下标只能是 0 或正数,这样负数如 -1 就能方便地用于标识失败。当我们后续需要更复杂样式匹配时,我们只需要替换 strindex,其他代码都保持不变。(标准库提供了一个类似 strindex 的函数 strstr,不过它返回的是指针而不是索引。)

给出那么多设计之后,填充程序的细节就很简单了。下面是全部内容,你可以看到各个部分如何结合在一起的。

#include <stdio.h&gt;
#define MAX_LINE 1000    /* 输入行的最大长度 */

int getline(char line[], int max);
int strindex(char source[], char searchfor[]);

char pattern = "ould";    /* 要查找样式 */

/* 找到所有匹配样式的行 */
main()
{
    char line[MAX_LINE];
    int found = 0;
    while (getline(line, MAX_LINE) &gt; 0)
        if (strindex(line, pattern) &gt;= 0) {
            printf("%s", line);
            found++;
        }
    return found;
}

/* getline: 把行存入s, 返回长度 */
int getline(char s[], int lim)
{
    int c, i;

    i = 0;
    while (--lim > 0 &amp;&amp; (c = getchar()) != EOF &amp;&amp; c != 'n')
        s[i++] = c;
    if (c == 'n')
        s[i++] = c;
    s[i] = '';
    return i;
}

/* strindex: 返回s中t的索引,没有则返回-1 */
int strindex(char s[], char t[])
{
    int i, j, k;

    for (i = 0; s[i] != ''; i++) {
        for (j=i, k=0; t[k]!='' &amp;&amp; s[j]==t[k]; j++, k++)
            ;
        if (k > 0 &amp;&amp; t[k] == '')
            return i;
    }
    return -1;
}

每个函数定义都有如下格式

返回类型  函数名(参数声明)

{

        声明和语句

}

一些部分可以省略最小的函数为

dummy() {}

什么也不做,什么也不返回。像这样什么都不做的函数,有时在程序开发过程中被用来占位。如果没有返回类型,则假定返回int。

一个程序其实就是一系列变量定义和函数定义的集合。函数之间通信通过参数和函数的返回值,以及外部变量完成。在源文件中,多个函数可以按任意顺序排列,而一个源程序也可以拆分多个文件,只要别把单个函数拆分开就行。

return 语句是把值从被调函数返回给调用者机制。任何表达式都能跟在return后面:

return 表达式;

如有必要,表达式会被转换成函数的返回类型表达式常用括号括起来,不过这是可选的。

调用者函数可以自由地忽略返回值。更进一步地说,return后面不必有表达式;这种情况下没有值被返回给调用者。而当执行到函数最末尾的右大括号时,控制流程也同样返回给了调用者,并且不带返回值。如果一个函数在某些地方返回值,但在另一些地方不返回值,这不会不合法,但可能是麻烦的信号。不管怎样,如果一个函数无法返回值,它的“值”一定是垃圾值。

上面的样式搜索程序从main中返回一个状态,即匹配次数。这个值对调用程序的环境来说是有价值的。

对于如何编译加载多个文件的C程序,不同系统不同机制例如,在UNIX系统下是用第一章提到的 cc 命令假设三个函数分别位于三个文件 main.cgetline.cstrindex.c中,则命令

cc main.c getline.c strindex.c

编译三个文件,并将目标代码分别放到 main.o, getline.o 和 strindex.o 三个文件内,然后将它们都加载到一个叫做 a.out可执行文件中。如果其中某个文件存在错误假设就是main.c,则这个文件可以(修改后再次)单独编译,并将编译结果和之前成功目标文件一起加载使用如下命令

cc main.c getline.o strindex.o

cc命令通过 “.c” 和 “.o”这样的命名规则来区分源文件和目标文件。

练习4-1:写函数 strrindex(s, t) ,返回 t 在 s 中右侧位置,若不包含则返回-1。

原文地址:https://blog.csdn.net/baluzju/article/details/134438061

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_44440.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注