前言的闲话以及第一章的入门(七)

来源:互联网 发布:js操作input file 编辑:程序博客网 时间:2024/06/05 16:12

1.8 Arguments - Call by Value

One aspect of C functions may be unfamiliar to programmers who are used to some other languages, particulary Fortran. In C, all function arguments are passed "by value.'' This means that the called function is given the values of its arguments in temporary variables rather than the originals. This leads to some different properties than are seen with "call by reference'' languages like Fortran or with var parameters in Pascal, in which the called routine has access to the original argument, not a local copy.

习惯其它语言(特别是Fortran语言)的程序员可能会对C 语言的函数参数传递方式感到陌生。在C 语言中,所有函数参数都是“通过值”传递的。也就是说,传递给被调用函数的参数值存放在临时变量中,而不是存放在原来的变量中。这与其它某些语言是不同的,比如,Fortran等语言是“通过引用调用”,Pascal则采用var参数的方式,在这些语言中,被调用的函数必须访问原始参数,而不是访问参数的本地副本。


#include <stdio.h>

int power(int m, int n);

/* test power function */

main()

{

    int i;

    for (i = 0; i < 10; ++i)

        printf("%d %d %d\n", i, power(2,i), power(-3,i));

    return 0;

}

/* power: raise base to n-th power; n >= 0 */

int power(int base, int n)

{

    int i, p;

    p = 1;

    for (i = 1; i <= n; ++i)

        p = p * base;

    return p;

}

Call by value is an asset, however, not a liability. It usually leads to more compact programs with fewer extraneous variables, because parameters can be treated as conveniently initialized local variables in the called routine. For example, here is a version of power that makes use of this property.

传值调用的利大于弊。在被调用函数中,参数可以看作是便于初始化的局部变量,因此额外使用的变量更少。这样程序可以更紧凑简洁。侧如,下面的这个power函数利用了这一性质:

/* power: raise base to n-th power; n >= 0; version 2 */

int power(int base, int n)

{

int p;

for (p = 1; n > 0; --n)

p = p * base;

return p;

}

 

The parameter n is used as a temporary variable, and is counted down (a for loop that runs backwards) until it becomes zero; there is no longer a need for the variable i. Whatever is done to n inside power has no effect on the argument that power was originally called with.

其中,参数n 用作临时变量,并通过随后执行的for 循环语句递减,直到其值为0,这样就不需要额外引入变量ipower 函数内部对n 的任何操作不会影响到调用函数中n 的原始参数值。

When necessary, it is possible to arrange for a function to modify a variable in a calling routine. The caller must provide the address of the variable to be set (technically a pointer to the variable), and the called function must declare the parameter to be a pointer and access the variable indirectly through it.

必要时,也可以让函数能够修改主调函数中的变量。这种情况下,调用者需要向被调用函数提供待设置值的变量的地址(从技术角度看,地址就是指向变量的指针),而被调用函数则需要将对应的参数声明为指针类型,并通过它间接访问变量.

The story is different for arrays. When the name of an array is used as an argument, the value passed to the function is the location or address of the beginning of the array - there is no copying of array elements. By subscripting this value, the function can access and alter any argument of the array.  

如果是数组参数,情况就有所不同了。当把数组名用作参数时,传递给函数的值是数组起始元素的位置或地址——它并不复制数组元素本身。在被调用函数中,可以通过数组下标访问或修改数组元索的值。




extraneous 外来的

asset 有利条件;优点

liability 妨碍, 不利




1.9 Character Arrays

The most common type of array in C is the array of characters. To illustrate the use of character arrays and functions to manipulate them, let's write a program that reads a set of text lines and prints the longest. The outline is simple enough:

字符数组是C 语言中最常用的数组类型。下面我们通过编写一个程序,来说明字符数组

以及操作字符数组的函数的用法。该程序读入一组文本行,并把最长的文本行打印出来。该

算法的基本框架非常简单:

 

while (there's another line)(还有未处理的行)

if (it's longer than the previous longest)(该行比已处理的最长行还要长)

 (save it)保存该行为最长行

 (save its length)保存该行的长度

 print longest line打印最长的行

 

This outline makes it clear that the program divides naturally into pieces. One piece gets a new line, another saves it, and the rest controls the process.

从上面的框架中很容易看出,程序很自然地分成了若干片断,分别用于读入新行、测试读入的行、保存该行,其余部分则控制这一过程。

 

Since things divide so nicely, it would be well to write them that way too. Accordingly, let us first write a separate function getline to fetch the next line of input. We will try to make the function useful in other contexts. At the minimum, getline has to return a signal about possible end of file; a more useful design would be to return the length of the line, or zero if end of file is encountered. Zero is an acceptable end-of-file return because it is never a valid line length. Every text line has at least one character; even a line containing only a newline has length 1.

因为这种划分方式比较合理,所以可以按照这种方式编写程序。首先,我们编写一个独立的函数getline,它读取输入的下一行。我们尽量保持该函数在其它场台也有用。至少

getline 函数应该在读到文件末尾时返回一个信号;更为有用的设计是它能够在读入文本行时返回该行的长度,而在遇到文件结束符时返回0。由于0 不是有效的行长度,因此可以作为标志文件结束的返回值。每一行至少包括一个字符,只包含换行符的行,其长度为1

When we find a line that is longer than the previous longest line, it must be saved somewhere. This suggests a second function, copy, to copy the new line to a safe place.

当发现某个新读入的行比以前读入的最长行还要长时,就需要把该行保存起来。也就是说,我们需要用另一个函数copy把新行复制到一个安全的位置。

 

Finally, we need a main program to control getline and copy. Here is the result.

最后,我们需要在主函数main中控制getlinecopy这两个函数。以下便是我们编写的程序:


#include <stdio.h>

#define MAXLINE 1000 /* maximum input line length */

int getline(char line[], int maxline);

void copy(char to[], char from[]);

/* print the longest input line */

main()

{

int len; /* current line length */

int max; /* maximum length seen so far */

char line[MAXLINE]; /* current input line */

char longest[MAXLINE]; /* longest line saved here */

max = 0;

while ((len = getline(line, MAXLINE)) > 0)

     if (len > max) {

max = len;

copy(longest, line);

     }

if (max > 0) /* there was a line */

printf("%s", longest);

return 0;

}

/* getline: read a line into s, return length */

int getline(char s[],int lim)

{

int c, i;

for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)

     s[i] = c;

if (c == '\n') {

     s[i] = c;

     ++i;

}

s[i] = '\0';

return i;

}

/* copy: copy 'from' into 'to'; assume to is big enough */

void copy(char to[], char from[])

{

int i;

i = 0;

while ((to[i] = from[i]) != '\0')

++i;

}

main and getline communicate through a pair of arguments and a returned value. In getline, the arguments are declared by the line

maingetline之间通过一对参数及一个返回值进行数据交换。在getline函数中,两个参数是通过程序行

 

int getline(char s[], int lim);

which specifies that the first argument, s, is an array, and the second, lim, is an integer. The purpose of supplying the size of an array in a declaration is to set aside storage. The length of an array s is not necessary in getline since its size is set in main. getline uses return to send a value back to the caller, just as the function power did. This line also declares that getline returns an int; since int is the default return type, it could be omitted.

声明的,它把第一个参数s 声明为数组,把第二个参数lim 声明为整型,声明中提供数组大小的目的是留出存储空间。在getline函数中没有必要指明数组s的长度,这是因为该数组的大小是在main函数中设置的。如同power函数一样,getline函数使用了一个return语句将值返回给其调用者。上述程序行也声明了getline数的返回值类型为int。由于函数的默认返回值类型为int,因此这里的int可以省略。

 

Some functions return a useful value; others, like copy, are used only for their effect and return no value. The return type of copy is void, which states explicitly that no value is returned.

有些函数返回有用的值,而有些函数(如copy)仅用于执行一些动作,并不返回值。copy函数的返回值类型为void,它显式说明该函数不返回任何值。

 

getline puts the character '\0' (the null character, whose value is zero) at the end of the array it is creating, to mark the end of the string of characters. This conversion is also used by the C language: when a string constant like

getline函数把字符'\0'(即空字符,其值为0)插入到它创建的数组的末尾,以标记字符串的结束。这一约定已被C语言采用:当在C语言程序中出现类似于

"hello\0"


The %s format specification in printf expects the corresponding argument to be a string represented in this form. copy also relies on the fact that its input argument is terminated with a '\0', and copies this character into the output.

printf函数中的格式规范%s规定,对应的参数必须是以这种形式表示的字符串。copy函数的实现正是依赖于输入参数由'\0'结束这一事实,它将'\0'拷贝到输出参数中。(也就是说,空字符'\0'不是普通文本的一部分。)

 


It is worth mentioning in passing that even a program as small as this one presents some sticky design problems. For example, what should main do if it encounters a line which is bigger than its limit? getline works safely, in that it stops collecting when the array is full, even if no newline has been seen. By testing the length and the last character returned, main can determine whether the line was too long, and then cope as it wishes. In the interests of brevity, we have ignored this issue.

值得一提的是,即使是上述这样很小的程序,在传递参数时也会遇到一些麻烦的设计问题。例如,当读入的行长度大于允许的最大值时,main 函数应该如何处理,getline 函数的执行是安全的,无论是否到达换行符字符,当数组满时它将停止读字符。main函数可以通过测试行的长度以及检查返回的最后一个字符来判定当前行是否太长,然后再根据具体的情况处理。为了简化程序,我们在这里不考虑这个问题。

 

There is no way for a user of getline to know in advance how long an input line might be, so getline checks for overflow. On the other hand, the user of copy already knows (or can find out) how big the strings are, so we have chosen not to add error checking to it.

调用getline 函数的程序无法预先知道输入行的长度,因此getline 函数需要检查是否溢出。另一方面,调用copy函数的程序知道(也可以找出)字符串的长度,因此该函数不需要进行错误检查。









 


原创粉丝点击