`
txf2004
  • 浏览: 6830713 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

《C程序设计语言》第四章 函数和程序结构

 
阅读更多

4.1 函数的基本知识

如果函数定义中省略了返回值类型,则默认为int类型。

练习4-1 编写函数strindex(s, t),它返回字符串t在s中最右边出现的位置。
如果s中不包含t,则返回-1。
答:
#include <stdio.h>
int strindex(char s[], char t[])
{
int i, j, k;
for (i = 0; s[i] != '\0'; i++) // to end of s
;
for (i = i - 1; i >= 0; i--) {
for (j = 0; t[j] != '\0'; j++) // to end of t
;
// compare s and t in reverse order
for (j = j - 1, k = i; j >= 0 && k >= 0; j--, k--)
if (s[k] != t[j])
break;
if (j == -1)
return k + 1;
}
return -1;
}
main()
{
char s[] = "thisiscdaiandrachel";
char t1[] = "cdai";
char t2[] = "xyz";
char t3[] = "this";
char t4[] = "elx";
printf("%d\n", strindex(s, t1));
printf("%d\n", strindex(s, t2));
printf("%d\n", strindex(s, t3));
printf("%d\n", strindex(s, t4));
}


4.2 返回非整型值的函数

由于atof函数的返回值类型不是int,因此该函数必须声明返回值的类型。
其次,调用函数必须知道atof函数返回的是非整型值。可以在调用函数中显示声明atof函数。
#include <stdio.h>
#define MAXLINE 100
main()
{
double sum, atof(char []);
char line[MAXLINE];
int getline(char line[], int max);
sum = 0;
while (getline(line, MAXLINE) > 0)
printf("\t%g\n", sum += atof(line));
return 0;
}

函数atof的声明与定义必须一致。如果atof函数与调用它的main函数放在同一源文件中,
并且类型不一致,编译器会检测到该错误。如果atof函数式单独编译的,这种不匹配的
错误就无法检测出来。

如果没有函数原型,则函数将在第一次出现的表达式中被隐式声明。函数的返回值将被
假定为int类型,但上下文并不对其参数作任何假设。

练习4-2 对atof函数进行扩充,使它可以处理形如123.45e-6的科学表示法。其中,浮点数
后面可能会紧跟一个e或E以及一个指数(可能有正负号)。
答:
#include <stdio.h>
#include <ctype.h>

/* atof: convert string s to double */
double atof(char s[])
{
double val, power, epow;
int i, sign, j, esign;
for (i = 0; isspace(s[i]); i++) /* skip white space */
;
// 1. Sign
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
i++;
// 2. Integer part
for (val = 0.0; isdigit(s[i]); i++)
val = 10.0 * val + (s[i] - '0');
// 3. Float part
if (s[i] == '.')
i++;
for (power = 1.0; isdigit(s[i]); i++, power *= 10)
val = 10.0 * val + (s[i] - '0');
// 4. E part
if (s[i] == 'e' || s[i] == 'E')
i++;
esign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
i++;
for (j = 0; isdigit(s[i]); i++) {
j = 10 * j + (s[i] - '0');
}
for (epow = 1.0; j > 0; j--) {
if (esign == -1)
epow /= 10;
else
epow *= 10;
}
return sign * val / power * epow;
}
main()
{
printf("%.2lf\n", atof("4.85E3"));
printf("%.2lf\n", atof("3.6"));
printf("%.2lf\n", atof("3.6e5"));
printf("%.2lf\n", atof("-3.6e5"));
printf("%.8lf\n", atof("-3.6e-5"));
printf("%.2lf\n", atof("3.6e12"));
}


4.3 外部变量

外部变量定义在函数之外,可以在许多函数中使用。
自动变量在其所在函数退出时消失,而外部变量是永久存在的。
C语言不允许在一个函数中定义其他函数,因此函数本身是外部的。
通过同一名字对外部变量的所有引用(即使这种引用来自于单独编译的不同函数)实际上都是引用同一个对象。


4.4 作用域

名字的作用域指的是程序中可以使用该名字的部分。
外部变量或函数的作用域从声明它的地方开始,到其所在文件的末尾结束。

例如:
main() { ... }

int sp = 0;
double val[MAXVAL];

void push(double f) { ... }
double pop(void) { ... }

在push与pop中不需进行任何声明就可以访问变量sp与val,但是这两个变量名不能用在main函数中,
push与pop函数也不能用在main函数中。

如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,
则必须在相应的变量声明中强制性地使用关键字extern。将外部变量的声明与定义严格区分开来很重要。

变量声明用于说明变量的类型,而变量定义除此之外还将引起存储器的分配。
上面外部变量sp和val[]的定义int sp = 0; double val[MAXVAL];将会分配存储单元;而声明extern int sp;
extern double val[];则不会。定义中必须制定数组的长度,但extern声明则不一定要指定。

在一个源程序的所有源文件中,一个外部变量只能在某个文件中定义一次,而其他文件可以通过extern
声明来访问它。


4.5 头文件

首先分割文件:
主函数main单独放在main.c中;
push与pop函数放在stack.c;
getop放在getop.c
将getch与ungetch函数放在getch.c中。
之所以分割成多个文件,主要是考虑在实际的程序中,它们分别来自于单独编译的库。

此外,必须考虑定义和声明在这些文件之间的共享问题。尽可能把共享的部分集中在一起,放在
头文件calc.h中。
对于某些中等规模的程序,最好只用一个头文件存放程序中各部分共享的对象。
较大的程序需要使用更多的头文件,我们需要精心地组织它们。


4.6 静态变量

文件stack.c中定义的变量sp与val以及文件getch.c中定义的变量buf与bufp,它们仅供其所在的
源文件中的函数使用,其他函数不能访问。用static限定外部变量与函数,可以将其后声明的对象
的作用域限定为被编译源文件的剩余部分,达到隐藏外部对象的目的。

static char buf[BUFSIZE];
static int bufp = 0;

int getch(void) { ... }
void ungetch(int c) { ... }

其他函数就不能访问变量buf与bufp,因此这两个名字不会和同一程序中的其他文件中的相同名字
相冲突。

static也可用于声明函数,则该函数名除了对该函数声明所在的文件可见外,其他文件都无法访问。

static也可用于声明内部变量。static类型的内部变量是一种只能在某个特定函数中使用但一直占据
存储空间的变量。

练习4-11 修改getop函数,使其不必使用ungetch函数。


4.7 寄存器变量

register声明告诉编译器,将register变量放在机器的寄存器中。但编译器可以忽略此选项。

register int x;
f(register unsigned m)


4.8 程序块结构

变量i与程序块外声明的i无关。

if (n > 0) {
int i;
}

自动变量(包括形参)可以隐藏同名的外部变量与函数。

int x;
int y;
f(double x)
{
double y;
}


4.9 初始化

在不进行显式初始化的情况下,外部变量和静态变量都将被初始化为0;
而自动变量和寄存器变量的初值为无用的信息。

对于外部变量与静态变量来说,初始化表达式必须是常量表达式,且只初始化一次;
对于自动变量与寄存器变量,则在每次进入函数或程序块时都将被初始化。

对于自动变量与寄存器变量,初始化表达式可以不是常量表达式,表达式可以包含任意
在此表达式之前已经定义的值,包括函数调用。

数组的初始化可以在声明的后面紧跟一个初始化表达式列表。

int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

当省略数组的长度时,编译器将把花括号中初始化表达式的个数作为数组的长度。
如果初始化表达式的个数比数组元素少,则没有初始化的元素将被初始化为0。
如果初始化表达式的个数比数组元素多,则是错误的。

字符数组的初始化比较特殊:可以用一个字符串来代替初始化表达式序列。

char pattern[] = "ould"; 等价于 char pattern[] = { 'o', 'u', 'l', 'd' };


4.10 递归

将一个数作为字符串打印的情况。数字是低位先于高位生成的,但必须以与此相反的次序打印。

#include <stdio.h>

/* printd: print n in decimal */
void printd(int n)
{
if (n < 0) {
puchar('-');
n = -n;
}
if (n / 10)
printd(n / 10);
putchar(n % 10 + '0');
}

递归并不节省存储器的开销,因为递归调用过程中必须在某个地方维护一个存储处理值的栈。
递归的执行速度并不快,但递归代码比较紧凑,并且比相应的非递归代码更易于编写和理解。
在描述树等递归定义的数据结构时使用递归尤为方便。

练习4-12 运用printd函数的设计思想编写一个递归版本的itoa函数,即通过递归调用把整数
转换为字符串。
答:
#include <stdio.h>
#include <math.h>

void itoa(int n, char s[])
{
static int i;
if (n / 10)
itoa(n / 10, s);
else {
i = 0;
if (n < 0)
s[i++] = '-';
}
s[i++] = abs(n) % 10 + '0';
s[i] = '\0';
}

main()
{
char s[10];
itoa(189271, s);
printf("%s\n", s);
itoa(-9012398, s);
printf("%s\n", s);
}

练习4-13 编写一个递归版本的reverse(s)函数,以将字符串s倒置。
答:

void reverser(char s[], int i, int len)
{
int c, j;
j = len - (i + 1);
if (i < j) {
c = s[i];
s[i] = s[j];
s[j] = c;
reverser(s, ++i, len);
}
}

本题不适合用递归方法来解决。


4.11 C预处理器

预处理器是编译过程中单独执行的第一个步骤。两个最常用指令:#include和#define指令。
以及其他的一些,如条件编译与带参数的宏。

文件包含

#include "文件名":在源文件所在位置查找文件
#include <文件名>

在大的程序中,#include指令是将声明捆绑在一起的较好的方法。保证了所有的源文件都具有
相同的定义和变量声明。

宏替换

#define 名字 替换文本

这是一种最简单的宏替换,后续所有出现名字记号的地方都将被替换为替换文本。
替换只对记号进行,对括在引号中的字符串不起作用。
替换文本可以是任意的,如:#define forever for(;;)

宏定义也可以带参数,这样可以对不同的宏调用使用不同的替换文本。
如#define max(A, B) ((A) > (B) ? (A) : (B))
则x = max(p+q, r+s)将被替换为x = ((p+q) + (r+s)) ? (p+q) : (r+s));





分享到:
评论

相关推荐

    C语言程序设计(PDF格式)

    第4章 函数 57 4.1 函数说明与返回值 57 4.1.1 函数的类型说明 57 4.1.2 返回语句 58 4.2 函数的作用域规则 60 4.2.1 局部变量 60 4.2.2 全局变量 61 4.2.3 动态存储变量 62 4.2.4 静态存储变量 63 4.3 函数的调用与...

    程序设计语言 (C语言)(常用函数说明)

    程序设计语言 (C语言)(常用函数说明) 第一章 C 语言的特点 2 一 C是中级语言 2 二 C是结构式语言 2 三 C语言功能齐全 2 四 C语言适用范围大 3 第二章 数据类型、变量和运算符 3 第三章 数据类型 5 一 整型(int) 6 ...

    C程序设计语言[第2版].pdf

    本书是由C语言的设计者Brian W. Kernighan和Dennis M.... 第4章 涵数与程序结构 第5章 指针与数组 第6章 结构 第7章 输入与输出 第8章 UNIX系统接口 附录A 参考手册 附录B 标准库 附录C 变更小结 索引

    C程序设计语言(第2版_新版)习题解答.pdf

    内容简介 本书对Brain W. Kernighan和Dennis M. Ritchie所著的《The C Programming Language》(第2版)的所有练习题都进行...第4章 函数与程序结构 第5章 指针与数组 第6章 结构 第7章 输入与输出 第8章 UNIX系统接口

    浙江大学何钦铭、颜晖所著《C语言程序设计(第4版)》,高等教育出版社2020年版,C语言程序设计(第4版)课件

    这本书以程序设计为主线,以编程应用为驱动,通过案例和问题引入内容,重点讲解程序设计的思想和方法,并结合相关的语言知识的介绍。全书主要包括3方面的内容:基本内容(数据表达、数据处理和流程控制)、常用算法...

    《C程序设计语言》(非扫描.新版)

    C程序设计语言 第1章 导言 1.1 入门 1.2 变量与算术表态式 ...第4章 函数与程序结构 第5章 指针与数组 第6章 结构 第7章 输入与输出 第8章 UNIX系统接口 附录A 参考手册 附录B 标准库 附录C 变更小结

    C程序设计语言(第2版·新版中文)

    《C程序设计语言》(第2版新版)原著即为C语言的设计者之一Dennis M.Ritchie和著名的计算机科学家Brian W.Kernighan合著的一本介绍C语言的权威经典著作。我们现在见到的大量论述C语言程序设计的教材和专著均以此书为...

    C程序设计 第四版 谭浩强 高清扫描版 带完整书签目录 加 学习辅导

    包括C程序设计(第四版)的高清扫描版pdf以及与该书配套的学习辅导一书的pdf。 《C程序设计(第四版)》是由谭浩强教授著、清华大学出版社出版的《C程序设计》是一本公认的学习C语言程序设计的经典教材。根据C语言的...

    C程序设计语言官方题解

    C程序设计语言官方题解。 第一张 导言 第二章 类型、运算符与表达式 第三章 控制流 第四章 函数与程序结构 第五章 指针与数组 第六章 结构 第七章 输入与输出 第八章 UNIX系统接口

    c语言程序设计基础课件_东北大学

    本课程的教学目标是通过学习用一种典型的程序设计语言——C语言,建立起程序设计的概念,初步掌握程序设计方法,掌握程序设计的基本方法和技巧,养成良好的程序设计风格,从而具备应用程序设计解决相关专业领域内...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar )

    第4章 函数与预处理 4.1 概述 4.2 定义函数的一般形式 4.2.1 定义无参函数的一般形式 4.2.2 定义有参函数的一般形式 4.3 函数参数和函数的值 4.3.1 形式参数和实际参数 4.3.2 函数的返回值 4.4 函数的调用 4.4.1 ...

    C程序设计语言(第2版·新版)

    第4章 函数与程序结构 4.1 函数的基本知识 4.2 返回非整数值的函数 4.3 外部变量 4.4 作用域规则 4.5 头文件 4.6 静态变量 4.7 寄存器变量 4.8 分程序结构 4.9 初始化 4.10 递归 4.11 C预处理程序 ...

    程序设计语言概念

    编辑本段主要内容程序设计语言为程序员写出一个好的程序提供了所需的抽象机制、组织原则以及控制结构。本书所介绍的是在程序设计语言中出现的概念,即在程序设计语言的实现过程中产生的问题,以及语言的设计方式对...

    3.C++程序设计语言.第1~3部分.原书第4版 中文版有详细书签2016

    《C 程序设计语言》(原书第4版)是C 领域最经典的参考书,介绍了C 11的各项新特性和新功能。全书共分四部分。第一部分(第1~5章)是引言,包括C 的背景知识,C 语言及其标准库的简要介绍;第二部分(第6~15章)介绍...

    C程序设计语言 很适合初学者和再学者学习和复习

    第4章 函数与程序结构 4.1 函数的基本知识 4.2 返回非整数值的函数 4.3 外部变量 4.4 作用域规则 4.5 头文件 4.6 静态变量 4.7 寄存器变量 4.8 分程序结构 4.9 初始化 4.10 递归 4.11 C预处理程序 ...

    C程序设计语言_第2版(带书签目录)

    第四章 函数与程序结构 4.1 函数的基本知识 4.2 返回非整数型的函数 4.3 外部变量 4.4 作用域规则 4.5 头文件 4.6 静态变量 4.7 寄存器变量 4.8 程序块结构 4.9 初始化 4.10 递归 4.11 C预处理 第五章 ...

    C语言程序设计(高清PDF)

    第4章 函数 57 4.1 函数说明与返回值 57 4.1.1 函数的类型说明 57 4.1.2 返回语句 58 4.2 函数的作用域规则 60 4.2.1 局部变量 60 4.2.2 全局变量 61 4.2.3 动态存储变量 62 4.2.4 静态存储变量 63 4.3 函数的调用与...

    C语言精典版本C程序设计语言

    第4章 函数与程序结构 4.1 函数的基本知识 4.2 返回非整数值的函数 4.3 外部变量 4.4 作用域规则 4.5 头文件 4.6 静态变量 4.7 寄存器变量 4.8 分程序结构 4.9 初始化 4.10 递归 4.11 C预处理程序 ...

    C语言程序设计标准教程

    由于采用了函数模块式的结构, C语言易于实现结构化程序设计。使程序的层次结构清晰,便于程序的编写、阅读、调试。  在C语言中可从不同的角度对函数分类。 1. 从函数定义的角度看,函数可分为库函数和用户...

Global site tag (gtag.js) - Google Analytics