1函数
函数也被称为子程序,子功能
子程序:一个大型程序中的某部分代码,由一个或多个语句快组成,可以完成某项目的任务,
C语言函数分为库函数和自定义函数
2库函数
①在开发过程中每个程序员都会用到比如:printf,strcopy,为了支持可移植性和提高程序的效率,所以C语言在基础库中提供了一系列类似的库函数,方便程序员进行软件开发
②C语言常用的库函数
IO函数
字符串操作函数
字符操作函数
内存操作函数
时间/日期函数
数学函数
其他库函数
3自定义函数
3.1函数的基本用法
①自定义函数和库函数一样,由函数名,返回值类型和函数参数组成
//函数的形式
函数返回值 函数名(形式参数)
{
函数体;
}
//求和的基本方法
#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("请输入操作数:\n");
scanf("%d%d", &num1, &num2);//&num1称为取地址num1
sum = num1 + num2;
printf("%d\n", sum);
return 0;
}
//使用函数求和
#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
int Add(int num1, int num2){
int sum = 0;
sum = num1 + num2;
return sum;//如果返回类型是void类型,不能return返回而要用printf打印
}
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("请输入两个操作数:\n");
scanf("%d%d", &num1, &num2);
sum = Add(num1, num2);//函数的调用
printf("%d\n", sum);
}
!!函数名不能重复,区别于C++和JAVA
函数命名要用驼峰形式,首字母大写
②函数的作用:a)代码可以重复使用
b)便于维护
③函数的声明和定义
函数一般要放在主函数的上面,因为C语言是自上而下执行,如果被放在了后面就会报错,所以如果被放在了后面就要在主函数之前对函数定义
#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
int Add(int num1, int num2);//函数的定义,当函数放在主函数的后面时要对函数定义
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("请输入两个操作数:\n");
scanf("%d%d", &num1, &num2);
sum = Add(num1, num2);//函数的调用
printf("%d\n", sum);
}
int Add(int num1, int num2){
int sum = 0;
sum = num1 + num2;
return sum;
}
3.2实参和形参
实际参数(实参):真实传给函数的参数,实参可以是常量,变量,表达式,函数等,在函数调用的时候必须有固定的值传给形参
形式参数(形参):函数名后括号中的变量,形式参数只有在函数在调用的过程中被实例化,形参在函数调用完被自动销毁
void Swap1(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
//按值进行传递,形参的改变不影响实参
}
void Swap2(int *px, int *py)//*px是指针传递num1的地址①访问地址
{
int tmp = 0;
tmp = *px;//*px解引用,访问px保存的地址的内容②访问地址对应的内容
*px = *py;
*py = tmp;
return *px, *py;
//地址发生了交换
}
int main()
{
int num1 = 1;
int num2 = 2;
Swap1(num1, num2);
printf("调用Swap1交换后的值:%d %d\n", num1, num2);
Swap2(&num1, &num2);
printf("调用Swap2交换后的值:%d %d\n", num1, num2);
return 0;
}
//按值传递,形参的改变不会影响实参;按地址传递,形参会影响实参
Swap1和Swap2函数中的参数x,y,px,py都是形式参数,main函数中传递的num1,num2,&num1,&num2是实参
num1的值为1,地址为100;px保存着num1的地址100,px的地址为32
①Swap(&num1,&num2)把num1,num2的地址传给了指针变量px和py
②px,py里面放着num1和num2的地址100,200
②tmp =*px,*px:px变量里面保存的内容,*px解引用把指针变量保存的值给了tmp
使得其中的值发生了交换
3.3函数的链式调用
链式调用:把一个函数的返回值作为另外一个函数的参数
#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
int Add(int num1, int num2){
int sum = 0;
sum = num1 + num2;
return sum;
}
int Add3(int num1, int num2, int num3){
return Add(num1, num2) + num3;//函数的链式调用
}
int main()
{
int num1 = 0;
int num2 = 0;
int num3 = 0;
int sum = 0;
printf("请输入三个操作数:\n");
scanf("%d%d", &num1, &num2,&num3);
sum = Add3(num1,num2,num3);//函数的调用
printf("%d\n", sum);
}
3.4函数递归
递归:调用函数本身(把大问题改为小问题)
递归的条件:①调用自己本身②存在限制条件,当满足限制条件后,递归停;每次递归调用结束后都越来越接近限制条件
//使用库函数求字符串长度
int main()
{
char *str = "hello";
int len = strlen(str);//strlen()函数可以用来计算字符串的长度,遇到\0停止;\0和0都代表0,'0'代表48
printf("%d\n", len);
return 0;
}
调用函数实现求字符串长度
int MyStrlen(char *str)//*str的地址就是首字母()
{
if (*str == '\0'){
return 0;
}
else
{
return 1 + MyStrlen(str + 1);//Mystrlen(str+1)首字母右移
}
}
int main()
{
char *str = "hello";//""默认字符串后面有个\0
int len = MyStrlen(str);
printf("%d\n",len);
return 0;
}
3.5头文件,源文件,测试文件
①add.h只是声明
②add.c执行add.h,#include "add.h"把头文件引过来
③test.c测试文件
头文件
在这里插入代码片
源文件
测试文件
①头文件中的ifndef/define/endif的作用:预防头文件重复引入
②#include <add.h>和#include "add.h"的区别:#include <filename.h>:include目录下找;#include "filename.h"先去自己的头文件目录下找再去include目录下找
4结构体
//定义的一个学生类型结构体
/* struct Student//struct定义结构体的关键字
{
char name[20];
int age;
}stu2 = {"zhang",18};
*/
typedef struct Student
{
char name[20];
int age;
}stu2;//加上typedef是stu类型,在后续就可以定义变量
//结构体整体化只有一次机会,就是在定义的时候初始化
//结构体和数组一样,是聚合类型
int main()
{
stu2 s = { "zhang", 18 };
struct Student stu1 = { "caocao", 180 };//把学生类型结构体具体抽象成一个人,stu1 是变量
strcpy(stu1.name, "sun");//使用strcpy()函数对结构体已经定义的值修改,对stu1赋值只能赋一次,就是定义的时候,后续想修改只能通过strcpy函数一个一个修改
return 0;
}
练习
①斐波那契
//斐波那契前2项时1,从第三项开始时前两项之和
Feibonaiqie(int n)//递归求斐波那契
{
if (n == 1 || n == 2)
{
return 1;
}
else
return Feibonaiqie(n - 1) + Feibonaiqie(n - 2);
}
Feibonaiqie2(int n)//斐波那契数列
{
int i = 0;
int f1 = 1;
int f2 = 1;
int f3 = 0;
for (i = 3; i <= n; i++)//!!i一定要从3开始
{
f3 = f1 + f2;
f1 = f2;
f2 = f3;
}
return f3;
}
int main()
{
//printf("%d\n",Feibonaiqie(2));
//printf("%d\n", Feibonaiqie(3));
printf("%d\n", Feibonaiqie(9));
printf("%d\n", Feibonaiqie2(9));
return 0;
}
②汉诺塔
从A移到C上
void Move(char pos1, char pos2)
{
printf("%c->%c ", pos1, pos2);
}
void Hannuota(int n, char pos1, char pos2, char pos3)
{
if (n == 1)
{
Move(pos1,pos3);//如果只有一个直接移到第三个上面
}else
{
Hannuota(n-1,pos1,pos3,pos2);//把上面的n-1个先通过第三个柱子移到第二个上面
Move(pos1, pos3);//把第一个柱子上最下面的一个移到第三个上面
Hannuota(n - 1, pos2, pos1, pos3);//把第二个柱子上的所有通过第一个移到第三个上面达到目的
}
}
int main()
{
Hannuota(1,'A','B','C');
printf("\n");
Hannuota(4, 'A', 'B', 'C');
}
作业
①0-999999之间的水仙花数
int main()
{
//①判断i是几位数因为水仙花数要每一位立方之和②判断是不是水仙花
int i = 0;
for (i = 0; i <= 999999; i++)
{
int count = 0;
int tmp = i;//把i赋值给tmp,否则在判断完i的位数之后i就会变成0,赋值之后重复赋值
int sum = 0;
while (tmp != 0)
{
count++;
tmp = tmp / 10;//12:12/10,1/10=0得到i是两位数
}
tmp = i;
while (tmp != 0){
sum = sum + pow((double)(tmp %10), count);
tmp = tmp / 10;
}
if (sum == i){
printf("%d ", i);
}
}
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
void Menu()
{
printf("************\n");
printf("**猜字游戏**\n");
printf("***1.play***\n");
printf("***0.exit***\n");
printf("************\n");
}
void game()
{
int input = 0;
int randNum = rand() % 100;
while (1){
printf("请输入你要猜的数字:");
scanf("%d", &input);
if (input < randNum){
printf("你猜小了\n");
}else if (input > randNum){
printf("你猜大了\n");
}
else{
printf("你猜对了\n");
break;
}
}
}
int main()
{
int input = 0;
srand ((unsigned int)time(NULL));//设置随机种子使得每次产生的随机数不一样,每次运行的时候会获取当前电脑的时间,参数使NULL代表time的返回值不保存
do{
Menu();
printf("请老铁输入你的操作:");
scanf("%d", &input);
switch (input){
case 1:
game();
break;
case 0:
printf("拜拜\n");
break;
default:
printf("请重新输入\n");
break;
}
}while(input);
return 0;
}
③输入小写字符输出大写字符,输入大写字符输出小写字符,输入数字不输出
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
if (ch >= 'a'&&ch <= 'z')
{
putchar(ch - 32);
}
else if (ch >= 'A'&&ch <= 'Z'){
putchar(ch + 32);
}
}
}
④用函数实现初始化数组,清空数组,数组逆置,
void Init(int arr[], int set, int len)//初始化数组
{
int i = 0;
for (i = 0; i < len; i++)
{
arr[i] = set;
}
}
void Show(int arr[], int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
printf("%d", arr[i]);
}
printf("%\n");
}
void Reverse(int arr2[], int len)//数组逆置
{
int left = 0;
int right = len - 1;
while (left < right){
int tmp = arr2[left];
arr2[left] = arr2[right];
arr2[right] = tmp;
left++;
right--;
}
}
void Empty(int arr[], int len)//清空数组
{
int i = 0;
for (i = 0; i < len; i++)
{
arr[i] = 0;
}
}
int main()
{
int arr[10];
Init(arr, 2, 10);
Show(arr, 10);
int arr2[4] = { 1, 2, 3, 4 };
Reverse(arr2, 4);
Show(arr2,4);
Empty(arr, 10);
Show(arr, 10);
}
⑥乘法口诀
void ShowMuti(int n)
{
int i = 0;
for (i = 1; i <= n; i++)
{
int j = 1;
for (j = 1; j <= i; j++)
{
printf("%d*%d=%d ", i, j, i*j);
}
printf("\n");
}
}
int main()
{
int n = 0;
scanf("%d", &n);
ShowMuti(n);
return 0;
}
递归练习
①用递归实现字符串反向排列
void reverse_string(char *str)
{
assert(str != NULL);
char tmp = *str;
int len = strlen(str);
*str = *(str + len - 1);
*(str + len - 1) = '\0';
if (strlen(str + 1) >= 2)
reverse_string(str + 1);
*(str + len - 1) = tmp;
}
int main()
{
char str[10] = "hello";
reverse_string(str);
printf("%s", str);
}
②求数字各位数之和
int DigitSum(int n)
{
if (n > 9)
{
return DigitSum(n / 10) + n % 10;
}
else
{
return n % 10;
}
}
int main()
{
int ret = DigitSum(1728);
printf("%d\n", ret);
return 0;
}
③幂次方
int Pow(int n, int k)
{
if (k == 0)
{
return 1;
}
return n*Pow(n, k - 1);
}
int main()
{
int ret = Pow(2, 3);
printf("%d\n", ret);
return 0;
}
④求字符串长度
int my_strlen(const char *str)
{
int count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
int main()
{
const char *p = "zhang";
int len = my_strlen(p);
printf("len=%d\n", len);
return 0;
}