补充一下前面的内容:goto语句,c语言提供了可以随意滥用的goto语句和标记跳转的标号,理论上goto语句是没有必要的,但是某些场合下goto语句还是用的着的,最常见的用法是终止程序在某些深度嵌套的结构的处理过程,例如:一次跳出两层或多层循环,多层循环用break是达不到目的的。它只能从最内层的循环退出到上一层的循环。
下面是使用goto语句的一个例子:
一.函数是什么?
c语言中子程序是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码具有相对的独立性。
一般会输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
c语言的函数分类为1.库函数 2. 自定义函数
库函数是描述一些基础功能,他们不是业务性代码。是开发过程中每个程序员都可以用到的,方便可以进行软件开发。
学习库函数有几个网址
cplusplus.com - The C++ Resources Network
二.自定义函数
如果库函数能干所有事情,那就不需要程序员了,所以更加重要的是自定义函数。自定义函数和库函数一样,有函数名,返回值类型和函数参数。但是这都是我们自己来完成的,这给了我们很大的发挥空间。
举一个例子:
这是一个简单的加法函数。
让我们再看这个例子:
来思考一下这个函数为什么没有实现呢?
其实这涉及到实际参数(实参)和形式参数(形参)的问题。
真实传给函数的参数叫实参。
实参可以是:常量,变量,表达式,函数等。
无论是什么值,在函数进行调用时,它们都必须有确定的值,以便把这些值传给形参。
形参是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才会实例化(分配内存单元),所以叫形式参数,形式参数当函数调用完成后就自动销毁了。因此形式参数只在函数中有效。
这里的问题在于
所以这里的函数没起到作用。
那么我们该如何修改呢?
三 .函数的调用
3.1传值调用
函数的形参和实参分别占有不同的内存块,对形参的修改不会影响实参。
3.2传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外部的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
3.3练习
1.写一个函数可以判断100-200的素数
2.写一个函数判断1000-2000是不是闰年
#include<stdio.h>
int is_leap_year(i)
{
if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0)
{
return 1;
}
else
{
return 0;
}
}
int main()
{
int year = 0;
int count = 0;
for (year = 1000; year <= 2000; year++)
{
if (is_leap_year(year) == 1)
{
count++;
printf("%d ", year);
}
}
printf("\ncount = %d\n", count);
return 0;
}
3.写一个函数,实现一个整形有序数组的二分查找
#include<stdio.h>
int binary_search(int *arr, int k, int sz)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (right + left) / 2;
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
return mid;
}
return -1;
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int k = 0;
scanf("%d", &k);
int ret = binary_search(arr, k, sz);
if (ret == -1)
{
printf("找不到\n");
}
else
{
printf("找到了,下标为%d\n", ret);
}
return 0;
}
4.写一个函数,每次调用这个函数就会增加num的值。
四.函数的嵌套调用和链式访问
函数和函数之间可以根据实际的需求进行组合,也就是相互调用。
4.1嵌套调用
函数可以嵌套调用,但是不能嵌套定义。
4.2链式访问
把一个函数的返回值作为另外一个函数的参数。
printf函数返回的打印字符的个数,第一次打印43返回2,第二次打印2返回1,第三次打印1返回1。
五.函数的声明和定义
函数的声明是告诉编译器函数叫什么,参数是什么,返回类型是什么。
函数的声明一般出现在函数的使用之前,要满足先声明后使用。
函数的声明一般要放在头文件中。
test.h放置函数的声明
函数的定义是指函数的具体实现的内容,交代函数的功能实现。
test.c放函数的实现
六.函数的递归
程序调用自身的编程技巧称为递归。它通常把一个大的复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。只需要少量的程序就可以描述出解题过程中的多次重复的运算,大大减少了程序的代码量。
6.1递归的两个必要条件
1.存在限制条件,当满足这个条件时,递归便不再继续。
2.每次递归之后会越来越接近这个限制条件。(若产生死递归会导致栈溢出stackoverflow)
例子:接受一个无符号整型,按照顺序打印它的每一位
输入1234 输出1 2 3 4
递归的过程
例子2:
编写函数不允许创建临时变量,求字符串的长度
例子3:
求n的阶乘
例子4:
求第n个斐波那契数
1 1 2 3 5 8 13 21 34 55....
但是这样才用递归的方法会导致大量的重复运算。所以我们可以采用非递归的方法。
总结:
1.许多问题是以递归的形式进行解释的,只是因为它比非递归的形式更加简洁。
2.迭代实现比递归的效率更高,但是代码的可读性较差。
3.当一个相当难的问题难以用迭代实现时,此时递归实现的简洁性可以补偿它所带来的运行时的开销。