文档章节

C程序设计语言--第四章:函数与程序结构

fzyz_sb
 fzyz_sb
发布于 2013/09/28 16:13
字数 3008
阅读 96
收藏 0

函数可以把大的计算任务分解成若干个较小的任务,程序设计人员可以基于函数进一步构造程序,而不需要重新编写一些代码.一个设计得当的函数可以把程序中不需要了解的具体操作细节隐藏起来,从而使整个程序结构更加清晰,并降低修改程序的难度.

4.1 函数的基础知识

#include <stdio.h>
#define MAXLINE	1000

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

char pattern[] = "ould";

int main(void)
{
	char line[ MAXLINE ];
	int found = 0;

	while ( getline( line, MAXLINE )  > 0 ){
		if ( strindex( line, pattern ) >= 0 ){
			printf("%s", line );
			found++;
		}
	}

	return found;
}

int getline( char s[], int lim )
{
	int c, i;
	i = 0;
	while ( --lim > 0 && ( c = getchar() ) != EOF && c != '\n' ){
		s[ i++ ] = c;
	}
	if ( '\n' == c ){
		s[ i++ ] = c;
	}
	s[ i ] = '\0';
	return i;
}

int strindex( char s[], char t[] )
{
	int i, j, k;

	//这里j的使用非常的好.之前我都习惯用char *temp = s,然后通过temp进行比较,而这里直接用下标索引进行比较
	for ( i = 0; s[ i ] != '\0'; i++ ){
		for ( j = i, k = 0; t[ k ] != '\0' && s[ j ] == t[ k ]; j++, k++ ){
			;
		}
		if ( k > 0 && t[ k ] == '\0' ){
			return i;
		}
	}

	return -1;
}
习题4-1:
#include <stdio.h>
#include <string.h>

int strrindex( char *s, char *t )
{
	int lens = strlen( s );
	int lent = strlen( t );
	int i = 0;
	int j = 0;
	int k = 0;

	for ( i = lens - 1; i >= lent; i-- ){
		for ( j = i, k = lent - 1; k >= 0 && s[ j ] == t[ k ]; j--, k-- ){
			;
		}
		if ( -1 == k ){
			return i - lent;
		}
	}
	return -1;
}

int main(void)
{
	char s[] = "hello world i love this world and i love c++ too";
	char t[] = "world";
	int index = 0;
	index = strrindex( s, t );
	printf("%d\n", index );

	return 0;
}
程序输出:

4.2 返回非整型值的函数

#include <ctype.h>

double atof( char s[] )
{
	double val, power;
	int i, sign;

	for ( i = 0; isspace( s[ i ] ); i++ ){
		;
	}
	sign = ( '-' == s[ i ] ) ? -1 : 1;
	if ( '+' == s[ i ] || '-' == s[ i ] ){
		i++;
	}
	for ( val = 0.0; isdigit( s[ i ] ); i++ ){
		val = 10.0 * val + ( s[ i ] - '0' );
	}
	if ( '.' == s[ i ] ){
		i++;
	}
	for ( power = 1.0; isdigit( s[ i ] ); i++ ){
		val = 10.0 * val + ( s[ i ] - '0' );
		power *= 10.0;
	}

	return sign * val / power;
}
习题4-2:
#include <ctype.h>
#include <stdio.h>
#include <math.h>

double my_atof( char s[] )
{
	double val, power, exponent;
	int i, sign, powSign;

	for ( i = 0; isspace( s[ i ] ); i++ ){
		;
	}
	sign = ( '-' == s[ i ] ) ? -1 : 1;
	if ( '+' == s[ i ] || '-' == s[ i ] ){
		i++;
	}
	for ( val = 0.0; isdigit( s[ i ] ); i++ ){
		val = 10.0 * val + ( s[ i ] - '0' );
	}

	if ( '.' == s[ i ] ){
		i++;
	}
	for ( power = 1.0; isdigit( s[ i ] ); i++ ){
		val = 10.0 * val + ( s[ i ] - '0' );
		power *= 10.0;
	}

	if ( 'e' == s[ i ] ){
		i++;
	}
	powSign = ( '-' == s[ i ] ) ? -1 : 1;
	if ( '+' == s[ i ] || '-' == s[ i ] ){
		i++;
	}
	for ( exponent = 0; isdigit( s[ i ] ); i++ ){
		exponent = exponent * 10 + ( s[ i ] - '0' );
	}

	if ( -1 == powSign ){
		return sign * val / power / pow( 10.0, exponent );
	}

	return sign * val / power * pow( 10.0, exponent );
}

int main(void)
{
	char s[] = "123.45e-6";
	printf("%.10f\n", my_atof( s ) );

	return 0;
}
程序输出:

4.3 外部变量

形容词external与internal是相对的,internal用于描述定义在函数内部的函数参数及变量.外部变量定义在函数之外,因此可以在许多函数中使用.C语言不允许在一个函数中定义其他函数,因此函数本身是"外部的"(即函数可以被其他函数调用).默认情况下,外部变量与函数具有下列性质:通过同一个名字对外部变量的所有引用,实际上都是引用同一个对象.

外部变量的用途还表现在它们与内部变量相比具有更大的作用域和更长的生存期.自动变量只能在函数内部使用,从其所在的函数被调用时变量开始存在,在函数退出时变量也将消失.而外部变量是永久存在的,它们的值在一次函数调用到下一次函数调用之间保持不变.因此,如果两个函数必须共享某些数据,而这两个函数互补调用对方,这种情况下最方便的方式便是把这些共享数据定义为外部变量,而不是作为函数参数传递.

习题4-3,4-4,4-5:

main.h:

#include <ctype.h>
#define MAXVAL	100		//栈val的最大深度
#define NUMBER '0'
#define BUFSIZE 100		


int sp = 0;		//下一个空闲栈位置
double val[ MAXVAL ];	//堆栈
char buf[ BUFSIZE ];	//用于ungetch函数的缓冲区
int bufp = 0;			//buf中下一个空闲位置

int getch( void );
void ungetch( int );

void push( double f )
{
	if ( sp < MAXVAL ){
		val[ sp++ ] = f;
	}
	else{
		printf("error:stack full, can't push %g\n", f );
	}
}

double pop( void )
{
	if ( sp > 0 ){
		return val[ --sp ];
	}
	else{
		printf("error:stack empty\n");
		return 0.0;
	}
}

double top( void )
{
	if ( sp > 0 ){
		return val[ sp - 1 ];
	}
	else{
		printf("error:stack empty\n");
		return 0.0;
	}
}

void copyTopElement( double *dValue )
{
	*dValue = top();
}

void swapTopElement( void )
{
	double value1 = pop();
	double value2 = pop();
	push( value1 );
	push( value2 );
}

void clearStack( void)
{
	sp = 0;
}

int getop( char s[] )
{
	int i, c;
	while ( ( s[0] = c = getch() ) == ' ' || c == '\t' ){
		;
	}
	s[ 1 ] = '\0';
	if ( !isdigit( c ) && c != '.' ){
		return c;
	}
	i = 0;
	if ( isdigit( c ) ){
		while ( isdigit( s[ ++i ] = c = getch() ) ){
			;
		}
	}
	if ( '.' == c ){
		while ( isdigit( s[ ++i ] = c = getch() ) ){
			;
		}
	}
	s[ i ] = '\0';
	if ( c != EOF ){
		ungetch( c );
	}

	return NUMBER;
}

int getch( void )
{
	return ( bufp > 0 ) ? buf[ --bufp ] : getchar();
}

void ungetch( int c )
{
	if ( bufp >= BUFSIZE ){
		printf("ungetch: too many characters\n");
	}
	else{
		buf[ bufp++ ] = c;
	}
}
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "main.h"

#define MAXOP 100		//操作数或运算符的最大长度

int getop( char [] );
void push( double );
double pop( void );

int main(void)
{
	int type;
	double op2;
	char s[ MAXOP ];

	while ( ( type = getop( s ) ) != EOF ){
		switch( type ){
			case NUMBER:
				push( atof( s ) );
				break;
			case '+':
				push( pop() + pop() );
				break;
			case '*':
				push( pop() * pop() );
				break;
			case '-':
				op2 = pop();
				push( pop() - op2 );
				break;
			case '/':
				op2 = pop();
				if ( 0.0 != op2 ){
					push( pop() / op2 );
				}
				else{
					printf("error: zero divisor\n");
				}
				break;
			case '%':
				op2 = pop();
				if ( 0.0 != op2 ) {
					push( fmod( pop(), op2 ) );
				}
				else{
					printf("error: zero divisor\n");
				}
				break;
				/*p == pow*/
			case 'p':
				op2 = pop();
				push( pow( pop(), op2 ) );
				break;
				/*s == sin*/
			case 's':
				push( sin( pop() ) );
				break;
				/*e == exp*/
			case 'e':
				push( exp( pop() ) );
				break;
			case '\n':
				printf("\t%.8g\n", pop() );
				break;
			default:
				printf("error: unknown command %s\n", s );
				break;
		}
	}

	return 0;
}
程序输出:

习题4-6:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "main.h"

#define MAXOP 100		//操作数或运算符的最大长度

int getop( char [] );
void push( double );
double pop( void );

int main(void)
{
	int type, var, i;
	double op2;
	double v;
	char s[ MAXOP ];
	double variable[ 26 ];

	for ( i = 0; i < 26; i++ ){
		variable[ i ] = 0.0;
	}

	while ( ( type = getop( s ) ) != EOF ){
		switch( type ){
			case NUMBER:
				push( atof( s ) );
				break;
			case '+':
				push( pop() + pop() );
				break;
			case '*':
				push( pop() * pop() );
				break;
			case '-':
				op2 = pop();
				push( pop() - op2 );
				break;
			case '/':
				op2 = pop();
				if ( 0.0 != op2 ){
					push( pop() / op2 );
				}
				else{
					printf("error: zero divisor\n");
				}
				break;
			case '%':
				op2 = pop();
				if ( 0.0 != op2 ) {
					push( fmod( pop(), op2 ) );
				}
				else{
					printf("error: zero divisor\n");
				}
				break;
				/*p == pow*/
			case 'p':
				op2 = pop();
				push( pow( pop(), op2 ) );
				break;
				/*s == sin*/
			case 's':
				push( sin( pop() ) );
				break;
				/*e == exp*/
			case 'e':
				push( exp( pop() ) );
				break;
			case '=':
				pop();
				//我之前直接用op2 = pop()来代替var,但是由于pop返回的是double,直接把字符转换为了double,丢失了数据,所以必须要var来保存之前输入的字母
				if ( var >= 'A' && var <= 'Z' ){		
					variable[ var - 'A' ] = pop();
				}
				else{
					printf("error: no variable name\n");
				}
				break;
			case '\n':
				v = pop();
				printf("\t%.8g\n", v );
				break;
			default:
				if ( type >= 'A' && type <= 'Z' ){
					push( variable[ type - 'A' ] );
				}
				else if ( type == 'v' ){
					push( v );
				}
				else{
					printf("error: unknown command %s\n", s );
				}
				break;
		}
		var = type;
	}

	return 0;
}
main.h文件并为改变,程序输出:

习题4-7:

#include <string.h>
void ungets( char s[] )
{
    int len = strlen( s );
    while ( len > 0 ){
        ungetch( s[--len ] );
    }
}

习题4-8:

#include <stdio.h>

char buf = 0;

int getch( void )
{
	int c;
	if ( 0 != buf ){
		c = buf;
	}
	else{
		c = getchar();
	}
	buf = 0;

	return c;
}

void ungetch( int c )
{
	if ( 0 != buf ){
		printf("error: too many characters\n");
	}
	else{
		buf = c;
	}
}
习题4-9:

声明为int buffer[]即可.EOF不能被char所存储.

习题4-10:

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <math.h>

#define BUFSIZE 128

void pushStack( char *line, char **stack )
{
	char *token;

	token = strtok( line, " " );
	while ( NULL != token ){
		*stack = token;
		token = strtok( NULL, " " );
		stack++;
	}
}

double dealStack( char **stack, double val[] )
{
	int i = 0;
	int j = 0;
	char *str;
	double op2 = 0.0;
	double op1 = 0.0;
	while ( ( str = stack[ i++ ] ) != "" ){
		if ( isdigit( str[ 0 ] ) ){
			val[ j++ ] = atof( str );
		}
		else if ( '+' == str[ 0 ] ){
			op2 = val[ --j ];
			op1 = val[ --j ];
			val[ j++ ] = op1 + op2;
		}
		else if ( '-' == str[ 0 ] ){
			op2 = val[ --j ];
			op1 = val[ --j ];
			val[ j++ ] = op1 - op2;
		}
		else if ( '*' == str[ 0 ] ){
			op2 = val[ --j ];
			op1 = val[ --j ];
			val[ j++ ] = op1 * op2;
		}
		else if ( '/' == str[ 0 ] ){
			op2 = val[ --j ];
			op1 = val[ --j ];
			if ( 0 == op2 ){
				printf("error: zero divisor\n");
				break;
			}
			val[ j++ ] = op1 / op2;
		}
	}

	return val[ 0 ];
}

int main(void)
{
	double	val[ BUFSIZE ];
	char	*stack[ BUFSIZE ];
	char	line[ BUFSIZE ];
	int		i = 0;
	double	result = 0.0;

	for ( i = 0; i < BUFSIZE; i++ ){
		line[ i ] = 0;
		stack[ i ] = "";
		val[ i ] = 0.0;
	}


	while ( NULL != fgets( line, BUFSIZE, stdin ) ){
		pushStack( line, stack );

		result = dealStack( stack, val );

		printf("%f\n", result );
	}

	return 0;
}
程序输出:

4.4 作用域规则

名字的作用域指的是程序中可以使用该名字的部分.

外部变量或函数的作用域从声明它的地方开始,到其所在的文件的末尾结束.

如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,则必须在相应的变量声明中强制性的使用关键字extern.

在一个源程序的所有源文件中,一个外部变量只能在某个文件中定义一次,而其他文件可以通过extern声明来访问它.外部变量的定义中必须指定数组的长度,但extern声明则不一定要制定数组的长度.

4.6 静态变量

如果将变量或者函数声明了static,则变量和函数具有隐藏功能---即该变量和函数除了对它们声明所在的文件可见外,其他文件都无法访问.

习题4-11:

用一个静态局部变量存储即可.

4.7 寄存器变量

register声明告诉编译器,它所声明的变量在程序中使用频率较高.其思想是:将register变量放在机器的寄存器中,这样可以使程序更小,执行速度更快.但编译器可以忽略此选项.

register声明只适用于自动变量以及函数的形式参数.

func( register unsigned m, register long n )
{
	register int i;
	...
}
备注:无论寄存器变量实际上是不是存放在寄存器中,它的地址都是不能访问的.

4.8 程序块结构

C语言不允许在函数中定义函数.但是,在函数中可以以程序块结构的形式定义变量.变量的声明(包括初始化)除了可以紧跟在函数开始的花括号之后,还可以紧跟在任何其他标识符和语句开始的左花括号之后.以这种方式声明的变量可以隐藏程序块外与之同名的变量,它们之间没有任何关联.

#include <stdio.h>

int main( void )
{
	int i = 5;
	{
		int i = 6;
	}

	printf("%d\n", i );

	return 0;
}
程序输出: 5

在一个好的程序设计风格中,应该避免出现变量名隐藏外部作用域中相同名字的情况,否则,很可能引起混乱和错误.

4.9 初始化

在不进行显式初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初值则没有定义.对于外部变量与静态变量来说,初始化表达式必须是常量表达式,且只初始化一次(从概念上讲是在程序开始执行前进行初始化).

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

4.10 递归

#include <stdio.h>

void printd( int n )
{
	if ( n < 0 ){
		putchar( '-' );
		n = -n;
	}

	if ( n / 10 ){
		printd( n / 10 );
	}
	putchar( n % 10 + '0' );
}

int main(void)
{
	printd( -12345 );
	printf("\n");

	return 0;
}
程序输出:

习题4-12:

#include <stdio.h>

void itoa( int n, char *str, int base )
{
	static i = 0;		//这里用到数组的索引而不是用到指针是因为指针递增后找不到字符串数组的起始位置了.
	if ( n < 0 ){
		str[ i++ ] = '-';
	}
	if ( n / base ){
		itoa( n / base, str, base );
	}
	str[ i++ ] = n % base + '0';
	str[ i ] = '\0';		//这步有点浪费,如果数组初始化为'\0'就可以了.
}

int main(void)
{
	char	str[128];
	int		i = 0;
	for ( i = 0; i < 128; i++ ){
		str[ i ] = ' ';
	}
	itoa( 12345, str, 10 );

	printf("%s\n", str );

	return 0;
}
程序输出:

习题4-13:

#include <stdio.h>
#include <string.h>

void subreverse( char *str, int start, int end )
{	
	if ( start <= end ){
		char ch = str[ start ];
		str[ start++ ] = str[ end ];
		str[ end-- ] = ch;
		subreverse( str, start, end );
	}
}

void reverse( char *str )
{
	int len = strlen( str ) - 1;
	subreverse( str, 0, len );
}

int main(void)
{
	char	str[] = "hello world";
	reverse( str );

	printf("%s\n", str );

	return 0;
}
程序输出:

4.11 预处理器

预处理器是编译过程中单独执行的第一步骤.两个最常用的预处理器指令是:#include指令(用于在编译期间把指定文件的内容包含进当前文件中)和#define指令(用任意字符序列替代一个标记).

习题4-14:

#include <stdio.h>
#include <string.h>

#define swap( t, x, y ) { t temp; temp = x; x = y; y = temp; }

int main(void)
{
	int i = 3;
	int j = 4;
	float m = 5.5;
	float n = 6.6;
	swap( int, i, j );
	swap( float, m, n );

	printf("%d--%d\n", i, j );
	printf("%.2f--%.2f\n", m, n );

	return 0;
}
程序输出:


© 著作权归作者所有

共有 人打赏支持
fzyz_sb
粉丝 408
博文 209
码字总数 447144
作品 0
武汉
程序员
C语言书籍资料汇总

我汇总出自己收藏的C语言方面的书籍资料,方便后期使用,或许你也用的到。 以下内容,有链接的都可以下载。 一、书籍 元老级别的书籍: C程序设计语言.pdf (c语言之父) C Primer plus 第5...

BjarneCpp
2017/11/06
0
0
清华大学视频课件:面向对象程序设计(C++)(自主模式)

清华大学视频课件:面向对象程序设计(C++)(自主模式) 课程简介 C++是从C语言发展演变而来的一种面向对象的程序设计语言。面向对象的程序设计方法将数据及对数据的操作方法封装在一起,作...

dragonscroll
2017/11/16
0
0
软件入门的知识之程序设计语言Java和C#的简单介绍和对比[图]

软件入门的知识之程序设计语言Java和C#的简单介绍和对比[图]: 前言: 要做软件就必然会涉及到程序设计语言,它是什么?有哪些特点?又有哪几部分组成的呢?在这里我们为大家做了一个总结,希...

原创小博客
07/17
0
0
做游戏,学编程(C语言) 网易云课堂MOOC视频

应一些同学的要求,把这学期上C语言编程课的讲课视频录制剪辑,上传到网易云课堂,感兴趣的朋友可以在线观看,欢迎多提宝贵意见。 MOOC视频链接:http://study.163.com/course/introduction....

童晶
2017/11/07
0
0
《Python从小白到大牛》简介

本书是一部系统论述Python编程语言、OOP编程思想以及函数式编程思想的立体化教程(含纸质图书、电子书、教学课件、源代码与视频教程)。为便于读者高效学习,快速掌握Python编程方法。本书作...

tony关东升
06/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

活动招募 HUAWEI HiAI公开课·北京站-如何在4小时把你的APP变身AI应用

人工智能和机器学习是全球关注的新趋势,也是当前最火爆、最流行的话题。当你拿手机用语音助手帮你点外卖,智能推荐帮你把周边美食一网打尽;当你拿起P20拍照时,它将自动识别场景进行最美优...

华为终端开放实验室
36分钟前
1
0
匹配两位小数,js正则

var regex = /^\d*(\.[1-9]|\.\d[1-9])*$/ console.log(1.2,regex.test(1.2)); console.log(0.3,regex.test(0.3)); console.log(1.03,regex.test(1.03)); ......

微信小程序-暗潮
41分钟前
1
0
905. Sort Array By Parity - LeetCode

Question 905. Sort Array By Parity Solution 题目大意:数组排序,偶数放前,奇数在后,偶数的数之间不用管顺序,奇数的数之间也不用管顺序 思路:建两个list,一个放偶数,一个放奇数,最...

yysue
45分钟前
1
0
h5 禁止手机自带键盘弹出

html: <div style="width: 350px;margin:50px auto;"><input type="text" id="datePicker" class="date_picker form-control" placeholder="点击选择入住日期" /></div> js: $("#date......

Delete90
今天
1
0
color透明度对照表

透明度百分比 数值 100% 不透明 FF 95% F2 90% E6 85% D9 80% CC 75% BF 70% B3 65% A6 60% 99 55% 8C 50% 80 45% 73 40% 66 35% 59 30% 4D 25% 40 20% 33 15% 26 10% 1A 5% 0D 0% 完全透明 ......

_无问西东
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部