上次我们讲了函数的定义和函数调用的相关知识,现在我们来学习一下更正式的函数创建方法。
没有返回值的函数
有些函数是没有返回值的,它的返回类型为void,比如下面这串代码,和它的执行结果。
上面的puts_stars函数只是打印了几排 * 在电脑屏幕上,并没有进行其他的操作。其实,当我们在打印某些图形的时候,就没有返回值,比如打印一个直角三角形。
本函数只是用来进行显示的,因此没有需要返回结果。这种没有返回值的函数类型,要声明为viod。
不含形参的函数
输入一个正整数并显示其倒转后的值。
void Func(int n)
{
while (n > 0)
{
printf("%d",n%10);
n /= 10;
}
}
int main()
{
int n = 1234;
Func(n);
}
可以看到一般进行一下打印操作就不用有返回值。当然,其实我们写很多程序的时候也没有必要返回代码,当我们只是进行某些操作的时候,比如后面的使用指针操作变量等,很多地方就不许要返回值了。
函数原型声明
和我们人类一样,编译器在读取数据的时候,也是按照从头到尾的顺序依次读取的。从第一排一直想后读取数据,所以当我们的函数定义才main函数的大括号后的时候,就需要在main()之前进行声明。需要注意的是声明的时候要以分号结尾,相当一一条语句。
函数的嵌套与调用
在调用函数时有两种特殊的现象,即嵌套调用和递归调用。
1,嵌套调用
嵌套调用是指函数A中调用函数B,而函数B中又调用函数C。
上图(字有点丑)就是函数的嵌套调用的示意图。
函数嵌套调用示例
void A(int x);
void B(int x);
void C(int x);
int main()
{
int x = 10;
A(x);
return 0;
}
void A(int x)
{
printf("A收到传递过来的x=%d\n", x);
x++;
B(x);
}
void B(int x)
{
printf("B收到传递过来的x=%d\n", x);
x++;
C(x);
}
void C(int x)
{
printf("C收到传递过来的x=%d\n", x);
}
上述代码很好的说明了函数嵌套这种情况。
2.递归调用
递归调用是指一个函数直接或间接地调用自己。如果一个函数直接调用自己,称为直接递归;如果一个函数间接地调用自己,则称为间接调用。
我们先来写个阶乘的代码。
int Func(int n)
{
return n > 1 ? n+Func(n - 1) : 1;
}
int main()
{
int n = 10;
int sum =Func(n);
printf("%d", sum);
return 0;
}
这个代码很好的使用了递归的思想。
上面这张图就可以很好的解释递归的顺序。
使用递归的时候,一定要明确递归结束的条件,不然的话程序就会一直循环。
在程序设计实践中,递归程序的编写比较简单明了,但递归函数的每一层调用时,都要分配相应的存储空间,并完成参数的传递,函数的返回,在程序的执行效率和所消耗的存储空间上,和非递归的相比较没有任何优势。另外,再加上一般的递归都可以通过相应的方法转化为非递归的逻辑等价形式,所以并不推荐使用,特别是不推荐使用调用层次比较多的递归。比如说斐波那契数列如果用函数递归的话运行起来就会很慢。所以选择方法要合理。
宏定义及宏展开
宏定义了一个代表特定字符串内容的标识符。预处理过程会把源代码中出现的宏标识替换为宏定义时的字符串内容。宏最常见的用法是定义代表某个值的全局符号。宏的第二章用法是定义带参数的宏函数,这样的宏可以像函数一样被调用,但它是在调用语句处展开宏,并用调用时的实际参数来代替定义中的形式参数。
#define PI 3.1414926 //定义了一个名为PI的宏,其代表字符串3.1414926
宏的语法格式如下
#define 宏标识符 宏标识符代表的特定字符串
习惯上把宏定标识符都为大写,这样区分更加明显。
宏的优势在于 1 .使用方便,就上面的PI而言,在程序中如果一直使用3.1415926这样输入的话,时间久了容易写错。
2.可读性强,比如 PI 一眼就能看出它是数学上的圆周率,有更加明确的定义。
3.容易修改,当我们出于某些原因要修改PI的值的时候,我们只需要在宏定义里面修改即可,而不用需要在主函数里面一个一个的修改,大大提高了效率。
宏定义一般放在源程序的最前面,容易让别人看见,也便于修改。
宏定义也可以嵌套
#define PRICE 23.5
#define NUM 3
#define TOTAL (PRICE*NUM)
上述代码定义了一个名叫TOTAL的宏名,代表字符串(23.5*3)。
宏还可以定义一个字符串变量
#define VERSION "Hello World"
定义一个带参数的CUBE,即x*x*x。
#define CUBE(x) x*x*x
在定义带参数的宏时,要特别注意括号的使用。宏展开后应完全包含在一对括号中,而且参数也应包含在括号中,这样就保证了宏和参数的完整性,否则很容易出错。例如对于上面的CUBE宏,则CUBE(3+5)展开为3+5*3+5*3+5,是错误的,而正确的宏定义如下
#define CUBE(x) ((x)*(x)*(x))
宏和函数的区别在于带参数的宏和函数在用法上很相似,但它们存在本质的不同:首先宏只是在编译前进行简单的字符串替换,并不进行相应的诸如类型检查之类的语法出错检查,而函数定义与调用则会在编译时进行相应的语法检查;二是宏并不像函数一样存在给形参分配储存空间,完成实参向形参的参数传递,流程控制转移以及向被调用处返回值等过程,这样宏调用比函数调用的执行效率更高。但由于带参数的宏很容易发生副作用,所以并不推荐大家带参数的宏大量使用。
以上就是本节内容,有哪里讲的不好的,欢迎大家指正错误。