CPrimerPlus学习(九):函数/编程练习

带参数的函数

不能像普通变量声明那样使用同一类型的变量列表:
	void f(int x, y, z)           /* 无效的函数头 */
	void f(int x, int y, int z)   /* 有效的函数头 */
	函数中的这些变量被称为形式参数。

在调用时可写成 f(1,2,3)
	函数中的数值被称为实际参数。

简而言之
形式参数是被调函数(called function)中的变量,
实际参数是主调函数(calling function)赋给被调函数的具体值,
实际参数可以是常量、变量,或更复杂的表达式,
无论实际参数是何种形式都要被求值,然后该值被拷贝给被调函数相应的形式参数。

函数类型

声明函数时必须声明函数的类型;
带返回值的函数类型应该与其返回值类型相同,而没有返回值的函数应声明为void类型。

注意:函数类型指的是返回值的类型,不是函数参数的类型。

递归

//在一个函数内调用该函数本身
#include <stdio.h>

void up_and_down(int);

int main(void)
{
	up_and_down(1);

	return 0;
}

void up_and_down(int n)
{
	printf("Level %d: n location %p\n", n, &n);
	if (n < 4)
		up_and_down(n + 1);
	printf("LEVEL %d: n location %p\n", n, &n);
}

最简单的递归形式是把递归调用置于函数的末尾,即正好在 return 语句之前。
这种形式的递归被称为尾递归(tail recursion),因为递归调用在函数的末尾。
尾递归是最简单的递归形式,因为它相当于循环。

一般而言,选择循环比较好。
首先,每次递归都会创建一组变量,所以递归使用的内存更多,而且每次递归调用都会把创建的一组新变量放在栈中。递归调用的数量受限于内存空间。
其次,由于每次函数调用要花费一定的时间,所以递归的执行速度较慢。

递归既有优点也有缺点。
	优点是递归为某些编程问题提供了最简单的解决方案。
	缺点是一些递归算法会快速消耗计算机的内存资源。
	另外,递归不方便阅读和维护。

编译多源代码文件的程序

使用多个函数最简单的方法是把它们都放在同一个文件中,然后像编译只有一个函数的文件那样编译该文件即可。
其他方法因操作系统而异,下面将举例说明。

1、unix
	假定在UNIX系统中安装了UNIX C编译器cc(最初的cc已经停用,但是许多UNIX系统都给cc命令起了一个别名用作其他编译器命令,典型的是gcc或clang)。
	假设file1.c和file2.c是两个内含C函数的文件,下面的命令将编译两个文件并生成一个名为a.out的可执行文件:
		cc file1.c file2.c
	另外,还生成两个名为file1.o和file2.o的目标文件。
	如果后来改动了 file1.c,而file2.c不变,可以使用以下命令编译第1个文件,并与第2个文件的目标代码合并:
		cc file1.c file2.o
		
2、linux
	假定Linux系统安装了GNU C编译器GCC。
	假设file1.c和file2.c是两个内含C函数的文件,下面的命令将编译两个文件并生成名为a.out的可执行文件:
		gcc file1.c file2.c
	另外,还生成两个名为file1.o和file2.o的目标文件。
	如果后来改动了file1.c,而file2.c不变,可以使用以下命令编译第1个文件,并与第2个文件 的目标代码合并:
		gcc file1.c file2.o

3、DOS命令行编译器
	绝大多数DOS命令行编译器的工作原理和UNIX的cc命令类似,只不过使用不同的名称而已。
	其中一个区别是,对象文件的扩展名是.obj,而不是.o。
	一些编译器生成的不是目标代码文件,而是汇编语言或其他特殊代码的中间文件。
	
4、Windows和苹果的IDE编译器
	Windows和Macintosh系统使用的集成开发环境中的编译器是面向项目的。
	项目(project)描述的是特定程序使用的资源。资源包括源代码文件。 
	这种IDE中的编译器要创建项目来运行单文件程序。
	对于多文件程序,要使用相应的菜单命令。
	把源代码文件加入一个项目中。
	要确保所有的源代码文件都在项目列表中列出。
	许多IDE都不用在项目列表中列出头文件(即扩展名为.h的文件),因为项目只管理使用的源代码文件,源代码文件中的 #include指令管理该文件中使用的头文件。
	但是,Xcode要在项目中添加头文件。
	
5、头文件
	如果把main()放在第1个文件中,把函数定义放在第2个文件中,那么第1个文件仍然要使用函数原型。
	把函数原型放在头文件中,就不用在每次使用函数文件时都写出函数的原型。
	C 标准库就是这样做的,例如,把I/O函 数原型放在stdio.h中,把数学函数原型放在math.h中。
	你也可以这样用自定义的函数文件。
//头文件举例
//hook.h
#include <windows.h>

//假设以下函数在同一项目的key.cpp中
extern "C" __declspec(dllexport) BOOL StartHook(const char* windName, const char* user);
extern "C" __declspec(dllexport) BOOL StopHook(const char* user);

//hook.cpp
#include "hook.h" //注意使用""而不是<>
//接下来就可以在该文件中使用key.cpp中的函数

//#define也可以写在头文件中

查找地址:&运算符

一元&运算符给出变量的存储地址。
如果pooh是变量名,那么&pooh是变量的地址。
可以把地址看作是变量在内存中的位置。

假设有下面的语句:
	pooh = 24;
假设pooh的存储地址是0B76(PC地址通常用十六进制形式表示)。
那么下面的语句:
	printf("%d %p\n", pooh, &pooh);
将输出如下内容(%p是输出地址的转换说明):
	24 0B76

指针

从根本上看,指针(pointer)是一个值为内存地址的变量(或数据对象)。
正如char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。

假设一个指针变量名是ptr,可以编写如下语句:
	ptr = &pooh; // 把pooh的地址赋给ptr
对于这条语句,我们说ptr“指向”pooh。
ptr和&pooh的区别是ptr是变量, 而&pooh是常量。
或者,ptr是可修改的左值,而&pooh是右值。
还可以把ptr 指向别处:
ptr = &bah; // 把ptr指向bah,而不是pooh
现在ptr的值是bah的地址。
要创建指针变量,先要声明指针变量的类型。假
设想把ptr声明为储存 int类型变量地址的指针,就要使用下面介绍的新运算符。

间接运算符:*

示例:
	nurse = 22;
	ptr = &nurse; // 指向nurse的指针
	val = *ptr;  // 把ptr指向的地址上的值赋给val
不能写成 val = ptr ;
执行以上3条语句的最终结果是把22赋给val。

指针的声明示例:
	int * pi;   // pi是指向int类型变量的指针
	char * pc;    // pc是指向char类型变量的指针
	float * pf, * pg; // pf、pg都是指向float类型变量的指针
	
	//int * pi; 声明的意思是pi是一个指针,*pi是int类型

编程练习

1、
设计一个函数min(x, y),返回两个double类型值的较小值。
在一个简单的驱动程序中测试该函数。

#include <stdio.h>

double min(x,y);

int main(void)
{
	double x, y;
	double m;

	printf("Enter first number: ");
	scanf("%lf", &x);
	printf("Enter second number: ");
	scanf("%lf", &y);

	m = min(x, y);
	printf("the smaller number is %lf\n", m);

	return 0;
}

double min(double x, double y)
{
	if (x > y)
		return y;
	else
		return x;
}

2、
设计一个函数chline(ch, i, j),打印指定的字符j行i列。
在一个简单的驱动程序中测试该函数。

#include <stdio.h>

void chline(ch, i, j);

int main(void)
{
	int x, y;
	char ch;

	printf("Enter a letter: ");
	ch = getchar();

	printf("rows: ");
	scanf("%d", &y);
	printf("columns: ");
	scanf("%d", &x);

	chline(ch, x, y);

	return 0;
}

void chline(char ch, int i, int j)
{
	for (int m = 0; m < j; m++)
	{
		for (int n = 0; n < i; n++)
		{
			printf("%c ", ch);
		}
		printf("\n");
	}
}

在这里插入图片描述
3、
编写一个函数,接受3个参数:一个字符和两个整数。
字符参数是待打印的字符,第1个整数指定一行中打印字符的次数,第2个整数指定打印指定字符的行数。
编写一个调用该函数的程序。

//没看出来和第二题有啥区别

4、
两数的调和平均数这样计算:先得到两数的倒数,然后计算两个倒数的平均值,最后取计算结果的倒数。
编写一个函数,接受两个double类型的参数,返回这两个参数的调和平均数。

#include <stdio.h>

double harmonic(x, y);

int main(void)
{
	double x, y;
	double h;

	printf("Enter first number: ");
	scanf("%lf", &x);
	if (x == 0)
	{
		printf("Please re-enter!\n");
		printf("Enter first number: ");
		scanf("%lf", &x);
	}

	printf("Enter second number: ");
	scanf("%lf", &y);
	if (y == 0)
	{
		printf("Please re-enter!\n");
		printf("Enter second number: ");
		scanf("%lf", &y);
	}

	h = harmonic(x, y);
	printf("the harmonic mean of the two number is %lf\n", h);

	return 0;
}

double harmonic(double x, double y)
{
	double a, b;
	double mean;
	double harmonic;

	a = 1 / x;
	b = 1 / y;
	mean = (a + b) / 2;
	harmonic = 1 / mean;

	return harmonic;
}

在这里插入图片描述
5、
编写并测试一个函数larger_of(),该函数把两个double类型变量的值替换为较大的值。
例如, larger_of(x, y)会把x和y中较大的值重新赋给两个变 量。

#include <stdio.h>

double large_of(double x, double y);

int main(void)
{
	double x, y;
	double max;

	printf("Enter first number: ");
	scanf("%lf", &x);
	printf("Enter second number: ");
	scanf("%lf", &y);

	x = large_of(x, y);
	y = large_of(x, y);

	printf("x = %lf y = %lf\n", x,y);

	return 0;
}

double large_of(double x, double y)
{
	double max;

	if (x > y)
		max = x;
	else
		max = y;

	return max;
}

/*
Enter first number: 12
Enter second number: 26
x = 26.000000 y = 26.000000
*/

6、
编写并测试一个函数,该函数以3个double变量的地址作为参数,
把最小值放入第1个函数,中间值放入第2个变量,最大值放入第3个变量。
参考

#include <stdio.h>

void p(double * p1, double * p2, double * p3);

int main(void)
{
	double x, y, z;

	while ((scanf("%lf %lf %lf", &x, &y, &z)) == 3)
		p(&x, &y, &z);

	return 0;
}

void p(double * p1, double * p2, double * p3)
{
	double t;
	if (*p1 > *p2)
	{
		t = *p1;
		*p1 = *p2;
		*p2 = t;
	}
	if (*p1 > *p3)
	{
		t = *p1;
		*p1 = *p3;
		*p3 = t;
	}
	if (*p2 > *p3)
	{
		t = *p2;
		*p2 = *p3;
		*p3 = t;
	}
	printf("%lf %lf %lf\n", *p1, *p2, *p3);
}

7、
编写一个函数,从标准输入中读取字符,直到遇到文件结尾。
程序要报告每个字符是否是字母。
如果是,还要报告该字母在字母表中的数值位置。
例如,c和C在字母表中的位置都是3。
合并一个函数,以一个字符作为参数,
如果该字符是一个字母则返回一个数值位置,否则返回-1。

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

int alpha(ch);

int main(void)
{
	char ch;

	while ((ch = getchar()) != EOF)
		alpha(ch);

	return 0;
}

int alpha(char ch)
{
	int i;

	if (isalpha(ch))
		i =  toupper(ch) - 64;
	else
		i = -1;

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

/*
1a2b3c4d5e6f7g8h
-1
1
-1
2
-1
3
-1
4
-1
5
-1
6
-1
7
-1
8
-1
^Z
*/

8、
第6章的程序清单6.20中,power()函数返回一个double类型数的正整数次幂。
改进该函数,使其能正确计算负幂。
另外,函数要处理0的任何次幂都为0,任何数的0次幂都为1(函数应报告0的0次幂未定义,因此把该值处理为1)。
要使用一个循环,并在程序中测试该函数。

//程序清单6.20 powwer.c程序
// power.c -- 计算数的整数幂

#include <stdio.h>

double power(double n, int p); // ANSI函数原型

int main(void)
{
	double x, xpow;
	int exp;

	printf("Enter a number and the positive integer power");
	printf(" to which\nthe number will be raised. Enter q");
	printf(" to quit.\n");

	while (scanf("%lf%d", &x, &exp) == 2)
	{
		xpow = power(x, exp); // 函数调用
		printf("%.3g to the power %d is %.5g\n", x, exp, xpow);
		printf("Enter next pair of numbers or q to quit.\n");
	}
	printf("Hope you enjoyed this power trip -- bye!\n");

	return 0;
}

double power(double n, int p) // 函数定义
{
	double pow = 1;
	int i;

	for (i = 1; i <= p; i++)
		pow *= n;

	return pow; // 返回pow的值
}

/*
运行该程序后,输出示例如下:

Enter a number and the positive integer power to which
the number will be raised.Enter q to quit.
1.2 12
1.2 to the power 12 is 8.9161

Enter next pair of numbers or q to quit.
2 16
2 to the power 16 is 65536

Enter next pair of numbers or q to quit.
q
Hope you enjoyed this power trip -- bye!
*/
//修改
#include <stdio.h>
#include <math.h>

double power(double n, int p); // ANSI函数原型

int main(void)
{
	double x, xpow;
	int exp;

	printf("Enter a number and the positive integer power");
	printf(" to which\nthe number will be raised. Enter q");
	printf(" to quit.\n");

	while (scanf("%lf%d", &x, &exp) == 2)
	{
		if (x == 0 && exp!= 0)
			xpow = 0;
		else if (x != 0 && exp == 0)
			xpow = 1;
		else if (x == 0 && exp == 0)
		{
			printf("undefined 0^0\n");
			xpow = 1;
		}
		else
			xpow = power(x, exp); 

		printf("%.3g to the power %d is %.5g\n", x, exp, xpow);
		printf("Enter next pair of numbers or q to quit.\n");
	}
	printf("Hope you enjoyed this power trip -- bye!\n");

	return 0;
}

double power(double n, int p) // 函数定义
{
	double pow = 1;
	int i;

	if (p > 0)
	{
		for (i = 1; i <= p; i++)
			pow *= n;
	}
	else
	{
		for (i = 1; i <= abs(p); i++)
			pow *= n;
		pow = 1 / pow;
	}

	return pow; // 返回pow的值
}

/*
运行该程序后,输出示例如下:

Enter a number and the positive integer power to which
the number will be raised. Enter q to quit.
2 -3
2 to the power -3 is 0.125

Enter next pair of numbers or q to quit.
0 0
undefined 0^0
0 to the power 0 is 1

Enter next pair of numbers or q to quit.
3 0
3 to the power 0 is 1

Enter next pair of numbers or q to quit.
0 3
0 to the power 3 is 0

Enter next pair of numbers or q to quit.
2 3
2 to the power 3 is 8

Enter next pair of numbers or q to quit.
5 -2
5 to the power -2 is 0.04

Enter next pair of numbers or q to quit.
q
Hope you enjoyed this power trip -- bye!
*/

9、
使用递归函数重写编程练习8。

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

double power(double n, int p); // ANSI函数原型

int main(void)
{
	double x, xpow;
	int exp;

	printf("Enter a number and the positive integer power");
	printf(" to which\nthe number will be raised. Enter q");
	printf(" to quit.\n");

	while (scanf("%lf%d", &x, &exp) == 2)
	{
		if (x == 0 && exp!= 0)
			xpow = 0;
		else if (x != 0 && exp == 0)
			xpow = 1;
		else if (x == 0 && exp == 0)
		{
			printf("undefined 0^0\n");
			xpow = 1;
		}
		else
			xpow = power(x, exp); 

		printf("%.3g to the power %d is %.5g\n", x, exp, xpow);
		printf("Enter next pair of numbers or q to quit.\n");
	}
	printf("Hope you enjoyed this power trip -- bye!\n");

	return 0;
}

double power(double n, int p) // 函数定义
{
	double pow = 1;
	int i;

	if (p > 0)
	{
		for (i = 1; i <= p; i++)
			pow *= n;
	}
	else
	{
		pow = 1 / power(n, -p);
	}

	return pow; // 返回pow的值
}

10、
为了让程序清单9.8中的to_binary()函数更通用,
编写一个to_base_n() 函数接受两个在2~10范围内的参数,然后以第2个参数中指定的进制打印第 1个参数的数值。
例如,to_base_n(129, 8)显示的结果为201,也就是129的八进制数。
在一个完整的程序中测试该函数。

/* Programming Exercise 9-10 */

#include <stdio.h> 

void to_base_n(int x, int base); 

int main(void) 
{   
	int number;   
	int b;  
	int count;   

	printf("Enter an integer (q to quit):\n");  
	while (scanf("%d", &number) == 1)  
	{      
		printf("Enter number base (2-10): ");  
		while ((count = scanf("%d", &b))== 1  &&  (b < 2 || b > 10))  
		{        
			printf("base should be in the range 2-10: ");  
		}   
		if (count != 1)     
			break;     
		printf("Base %d equivalent: ", b);  
		to_base_n(number, b); 
		putchar('\n');    
		printf("Enter an integer (q to quit):\n"); 
	}  
	printf("Done.\n");  
	
	return 0;
} 

void to_base_n(int x, int base) 
{
	int r;

	r = x % base;  

	if (x >= base)  
		to_base_n(x / base, base);
	putchar('0' + r);   
		
	return;
}

11、
编写并测试Fibonacci()函数,该函数用循环代替递归计算斐波那契数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值