1.函数是什么
子程序,由一个或者多个语句组成,完成特殊的任务,较其他相比有独立性
2.函数分类
-
库函数
经常需要使用的功能包装成库函数
-
自定义函数
返回类型 函数名 (参数)
{
函数体
}
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void Swap(int* x, int* y)
{
int z;
z = *x;
*x = *y;
*y = z;
}
int main()
{
int a = 0, b = 0;
scanf("%d %d", &a, &b);
printf("交换前a =%d b=%d\n", a, b);
Swap(&a, &b);
printf("交换后a =%d b=%d\n", a, b);
return 0;
}
-
3函数的参数
1实参
真实传给函数的参数,参数可以是常量,变量,表达式,有返回值的函数等
如上面
Swap(&a, &b);
2形参
函数名后括号里面的变量,形式参数只有在给、函数调用时才实例化(分配内存单元);当函数调用完后就会自动销毁。
实参传递给形参时,形参时实参的临时拷贝
4函数调用
1传值调用
形参与实参在不同的内存中对形参进行修改不会影响实参
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
v
void Swap2(int x, int y)
{
int z;
z = x;
x = y;
y = z;
}
int main()
{
int a = 0, b = 0;
scanf("%d %d", &a, &b);
Swap2 (a, b);
printf("a =%d b=%d\n", a, b);
return 0;
}
2传址调用
是把函数外部创建变量的内存地址传递给函数参数,该方式可以将函数外与函数内联系起来,可以直接改变函数外部的变量。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void Swap1(int* x, int* y)
{
int z;
z = *x;
*x = *y;
*y = z;
}
int main()
{
int a = 0, b = 0;
scanf("%d %d", &a, &b);
printf("交换前a =%d b=%d\n", a, b);
Swap1 (&a, &b);
printf("交换后a =%d b=%d\n", a, b);
return 0;
}
3练习巩固
1打印100到200之间的素数
这个是错误的
#define _CRT_SECURE_NO_WARNINGS
//写一个函数判断输入是否为素数
#include<stdio.h>
int main()
{
int i = 0;
int flag = 1;//为什么flag在外面是错误的因为第一次执行flag变成0后面每次都是0
for (i = 100; i <= 200; i++)
{
int j = 0;
for (j = 2; j < i - 1; j++)
{
if (i % j == 0)
{
flag = 0;
break;
}
}
if (flag == 1)
{
printf("%d ", i);
}
}
return 0;
}
正确
#define _CRT_SECURE_NO_WARNINGS
//写一个函数判断输入是否为素数
#include<stdio.h>
int main()
{
int i = 0;
for (i = 100; i <= 200; i++)
{
int j = 0;
int flag = 1;
for (j = 2; j < i - 1; j++)
{
if (i % j == 0)
{
flag = 0;
break;
}
}
if (flag == 1)
{
printf("%d ", i);
}
}
return 0;
}
另一种优化
#define _CRT_SECURE_NO_WARNINGS
//写一个函数判断输入是否为素数
#include<stdio.h>
#include<math.h>
int main()
{
int i = 0;
//for (i = 100; i <= 200; i++)//偶数不可能是素数所以可以改成
for (i = 101; i <= 200; i+=2)
{
int j = 0;
int flag = 1;
for (j = 2; j <=sqrt(i); j++)/*m=a*b,如16=2*8/4*4,在前面有一个数一定<=sqrt(m),所以判断前面就可以*/
{
if (i % j == 0)
{
flag = 0;
break;
}
}
if (flag == 1)
{
printf("%d ", i);
}
}
return 0;
}
2写一个函数判断该数是不是素数
#define _CRT_SECURE_NO_WARNINGS
//写一个函数判断输入是否为素数
#include<stdio.h>
#include<math.h>
int is_prim(int n)
{
int j = 0;
for (j = 2; j <= sqrt(n); j++)
{
if (n % j == 0)
{
return 0;
}
}
return 1;
}
int main()
{
int i = 0;
scanf("%d", &i);
if (is_prim(i) )
{
printf("%d", i);
}
return 0;
}
3写一个函数判断该年是不是闰年
两种方式一种利用if判断一种利用逻辑运算符
#define _CRT_SECURE_NO_WARNINGS
//写一个函数判断是不是闰年
#include<stdio.h>
int is_leap_year1(int i)
{
int j = 0;
if (((i % 4 == 0) && (i % 100 != 0)) || (i % 400 == 0))
{
j = i;
}
return j;
}
int is_leap_year2(int i)
{
int j = 0;
if (i % 4 == 0)
{
if (i % 100 != 0)
{
j = i;
}
}
if (i % 400 == 0)
{
j = i;
}
return j;
}
int main()
{
int year = 0;
scanf("%d", &year);
if (is_leap_year2(year))
{
printf("%d", year);
}
return 0;
}
4写一个函数实现对一个整型数组进行二分查找
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int binary_search(int arr [],int k,int sz)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right)/ 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 };
int k = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
scanf("%d", &k);
if (binary_search(arr,k,sz)==-1)
{
printf("找不到\n");
}
else
{
printf("该数是第%d个数组\n", binary_search(arr, k, sz));
}
return 0;
}
实参数组传形参时是传递数组首元素的地址相当于指针变量
5写一个函数每次调用就会使num加一
#define _CRT_SECURE_NO_WARNINGS
//写一个函数每次调用就会使num加一
#include<stdio.h>
void Add(int* p);
int main()
{
int num = 0;
Add(&num);
printf("%d\n", num);
Add(&num);
printf("%d\n", num);
return 0;
}
void Add(int* p)
{
(*p)++;
}
5函数的嵌套调用与链式访问
5.1函数的嵌套调用
可以嵌套调用,但是不能嵌套定义;函数地位是相同的
#define _CRT_SECURE_NO_WARNINGS
//嵌套使用
#include<stdio.h>
void new_line();
void three_line();
int main()
{
three_line();
return 0;
}
void new_line()
{
printf("hh\n");
}
void three_line()
{
int i = 0;
for (size_t i = 0; i < 3; i++)
{
new_line();
}
}
5.2链式访问(前提是函数有返回值)
一个函数的返回值做另一个函数的参数
//链式访问
#include<stdio.h>
#include<string.h>
int main()
{
printf("%d", strlen("abc"));
return 0;
}
#include<stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
printf函数返回值是之前打印的个数
6函数的声明与定义
6.1函数的声明
告诉编译器函数是什么参数是什么返回类型是什么
6.2函数的定义
定义函数可以实现什么功能
7函数的递归
程序调用自身的编程技巧,把大事化小
输入整型值,按顺序打印每一位
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void print(unsigned int num);
int main()
{
unsigned int num = 0;
scanf("%u", &num);
print(num);
return 0;
}
void print(unsigned int num)
{
if (num > 9)
{
print(num / 10);
}
printf("%d ", num % 10);
}
递归的两个条件
1存在限制条件
2每次递归调用之后就越来越接近之前的限制条件
小练习
模拟strlen函数但不能创建临时变量
#define _CRT_SECURE_NO_WARNINGS
//模拟实现strlen
#include<stdio.h>
int my_strlen(char* str);
int main()
{
char arr [] = "abc";
//scanf("%s", &arr);
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
//
int my_strlen(char* str)
{
if (*str != '\0')
{
return 1 + my_strlen(str + 1);
}
else
return 0;
}
递归与迭代
在不同的情况下选择不同,有时迭代更效率
#include<stdio.h>
int fib(int a);
int main()
{
int a = 0;
scanf("%d", &a);
int ret = fib(a);
printf("%d\n", ret);
return 0;
}
//int fib(int a)
//{
// if (a <= 2)
// return 1;
// else
// return fib(a - 1) + fib(a - 2);
//}递归效率低‘
int fib(int n)
{
int a = 1, b = 1, c = 0;
while (n >= 3)
{
c = a + b;
a = b;
b = c;
n--;
}
return c;
}
栈溢出,可以使用非递归,或者将局部变量用static修饰