一、函数
子程序=部分代码-由一个或多个语句块组成,相较其他代码具有独立性
·一般输入会有返回值,提供对过程和细节的隐藏,通常被集成为软件库
函数分类
1、库函数
//经常使用的功能 代码效率提升
c.library
2、自定义函数
虚拟机-虚拟的一台电脑.vmdk
操作系统相关知识
io-输入输出
字符串-string.h
#include<string.h> int main() { char arr1[20] = { 0 }; char arr2[20] = { "hello dudu" }; strcpy(arr1, arr2); printf("%s\n",arr1); return 0; }
#include<string.h> int main() { char arr[20] = {"hello,word "}; memset(arr, 'x', 5); printf("%s\n", arr); return 0; }
库函数的使用必须包括头文件
//学习使用库函数
1、MSDN
(英文很重要,至少看懂文献)
2、自定义函数
程序员自己创造的函数
还是有函数名,返回值类型,函数参数
基本函数组成
ret_type fun name(para1, *) //返回类型 函数名 函数参数 { statement;//语句项 }
//比较两个数的大小(我写的 int compare(int x, int y) { int max; if(x>y) { max = x; } else { max = y; } printf("最大值为%d", max); } int main() { int a, b; scanf("%d,%d", &a, &b); compare(a, b); return 0; } //老师写的 int compare(int x, int y) { return(x > y ? x : y);//简化思想 } int main() { int a, b; scanf("%d,%d", &a, &b); int m=compare(a, b); printf("max=%d", m); return 0; }
//比较两个函数的最大值 void swap(int x, int y) { int ret; ret = x; x = y; y = ret; } //当实参传给形参时,形参是实参的临时拷贝 //对形参的修改不会影响实参 int main() { int a, b; scanf("%d %d", &a,&b); printf("交换前:%d %d",a, b); swap(a, b); printf("交换后:%d %d", a, b); return 0; } //这个代码的结果为: 2 3 交换前:2 3交换后:2 3 D:\yingyong\VS\xiangmu\penggec\Project3\Debug\Project3.exe (进程 39520)已退出,代码为 0。 按任意键关闭此窗口. . .
//比较两数大小(指针版 void swap(int* px, int* py) { int z = *px; *px = *py; *py = z; } int main() { int a=0, b=0; scanf("%d %d", &a, &b); printf("交换前:%d %d\n", a, b); swap(&a, &b); printf("交换后:%d %d", a, b); return 0; }
//什么时候传地址,什么情况下不需要传地址呢
//对值不需要进行修改,不需要改变变量的地址-不需要传地址
//一些小小的规划 时间待能把握住
//一个工程文件中能有多个.c文件,只能有一个main()函数
3、函数参数
3.1实参:真实传给函数的参数
函数的参数可以是函数
实参无论是何种类型的量,在进行调用时,必须有确定的值,以便把这些值传给形参。
3.2形参:函数名后括号中的变量
形式参数只有在函数被调用的过程中才实例化(分配内存单元
形式参数在调用后自动销毁,形参只在函数中有效
//形参实例化设置后,相当于实参的一份临时拷贝 ,对形参的修改不影响实参
4、函数的调用
4.1传值调用
传的是变量本身
4.2传址调用
传的是变量的地址,可以拿指针接收
把在函数外部创建的地址与函数创造了联系函数内部可以操作函数外部
//练习一下
//寻找素数 int main() { int count=0; int i = 0; for (i = 1; i < 200; i++) { int flag = 1; int j = 0; for (j = 2; j < i - 1; j++) { if (i % j == 0) { flag = 0; break; } } if (flag == 1) { count++; printf("%d \n", i); } } printf("%d", count); }
int main() { int count=0; int i = 0; for (i = 101; i < 200; i+=2) { int flag = 1; int j = 0; for (j = 2; j < sqrt(i); j++) { if (i % j == 0) { flag = 0; break; } } if (flag == 1) { count++; printf("%d \n", i); } } printf("%d", count); }
//判断是否是润年 int pandaun(int x)//定义函数不进行打印函数功能单一 { if(((x%4==0)&&(x / 100 != 0))|| (x / 400 == 0)) { return 0; } else { return 1; } } int main() { int a; printf("输入所需年份:"); scanf("%d", &a); int b=pandaun(a); if (b == 0) { printf("是润年"); } else { printf("不是闰年"); } }
//函数内部无法计算参数部分函数的值,数组传参传的是数组的首元素
//arr看上是数组实际上是指针
//bool类型 表示真假的变量
5、函数的嵌套调用和链式访问
5.1函数的嵌套
//函数可以嵌套调用但是不能嵌套定义
5.2链式访问
//依赖函数的返回值
//sizeof是用来获取变量或数据类型所占用的字节数的函数。
strlen是用来获取一个字符串的长度,即字符串中字符的数量(不包括结束符'\0')的函数
返回一个整型
//一个函数值的返回值作为另一个函数的参数叫做链式访问
int main() { printf("%d", printf("%d", printf("%d", 43))); //4321 //printf函数依据 return 0; }
//函数的返回值默认为int 不建议 直接定义函数不加其余内容容易混淆
//函数没写参数也可以给他传但是不建议,就算传了也不建议
也可以在函数中定义一个void
//main函数是有参数的
//如果是void的话无法进行链式访问
6、函数的声明和定义
6.1函数的声明
函数声明后 函数的定义 可以放在主函数后
//先声明后使用
int add(); int main() { int a = 5; int b = 10; int c = add(a, b); printf("%d",c); return 0; } int add(int x, int y) { return(x + y); }
函数的声明放在头文件中
//库函数用<>,自己写的函数时候使用” “就可以
一般写代码是有规划的
// 方便拆分模块 方便团队协作
//对开发的一个保密作用法
6.2函数的定义
int add(int x, int y) { return(x + y); }
//其他的一些内容
//关于静态库的内容 鹏哥 p44
//头文件声明定义的直接形式
7、函数递归
递归:程序调用自生的编程技巧
//递归:把大事化小
//%d打印有符号的整数(+-数)
//%u打印无符号整数
void print(unsigned int n) { if (n > 9) { print(n/10); } printf("%d ",n % 10); } int main() { unsigned int num= 0; scanf("%d", &num); print(num); return 0; }
//递是一个动作
//归为一个动作 两个相对独立的个体
//栈溢出
栈区 | 局部变量 函数的形参 函数的返回值 函数的调用都会在栈区上申请空间 栈区空间有限 无条件会栈溢出 (先进后出) |
---|---|
堆区 | |
静态区 |
递归条件
1、需要的现实条件
2、每次递归都必须更加接近现实条件
//求字符串的长度 void print(unsigned int n) { if (n > 9) { print(n/10); } printf("%d ",n % 10); } int main() { unsigned int num= 0; scanf("%d", &num); print(num); return 0; }
//不创建临时变量,求字符串的长度 int my(char*str) { int count=0; while (*str != "\0") { count++; str++;//找下一个字符 } return count; } int main() { char arr[] = "abc"; int len = my(arr); //abc/0//数组名是首字符的地址 printf("%d\n", len); return 0; }
递归与迭代
//递归实现 fac(int n) { if (n <= 1) return 1; else return n*fac(n-1); } int main() { int n = 0; scanf("%d", &n); int ret = fac(n); printf("ret=%d", ret); return 0; }
//迭代实现 fac(int n) { int i = 0; int ret = 1; for ( i = 0; i <= n; i++) { ret*= i; } return ret; } int main() { int n = 0; scanf("%d", &n); int ret = fac(n); printf("ret=%d", ret); return 0; }
//求斐波那契数列递归//效率低下 //斐波那契数列:前两个数等于第三个数 int count = 0; int fib(int n) { if (n = 3) count++; if (n <= 2) return 1; else return fib(n - 1) + fib(n - 2); } int main() { int n; scanf("%d", &n); int ret = fib(n); printf("%d\n", ret); printf("%d", count); return 0; }
//求斐波那契数列迭代 //斐波那契数列:前两个数等于第三个数 int fib(int n) { int a = 1; int b = 1; int c = 1; while (n>=3) { c = a + b; a = b; b = c; n--; } return c; } int main() { int n; scanf("%d", &n); int ret = fib(n); printf("%d\n", ret); return 0; }
//有时候递归解决问题不方便(效率低下等
/递归层次太深会出现栈溢出的情况
栈溢出解决方案:
//汉诺塔问题 //思路:把n-1层放到塔2,把n层放到塔3 void move(char x,char y) { printf("move from %c to %c \n",x,y); } void hanoi(int n, char left, char mid, char right) { if (n == 1) { move(left, right); } else { hanoi(n - 1, left, mid, right); move(left, right); hanoi(n - 1, mid, left, right); } } int main() { int n; scanf("%d", &n); hanoi(n,'a','b','c'); return 0; }
//青蛙跳台阶问题 int jump(int m) { if (m == 1) return 1; else if (m == 2) return 2; else return (jump(m - 1)*2); } int main() { int n; scanf("%d", &n); int ret = jump(n); printf("%d", ret); return 0; }
//插入一段作业讲解
1、死循环打印
2、if语句可以实现单分支也可以实现多分支,并不是指和对齐的语句匹配
if语句中不一定1为真0为假(条件限制不同
3、switch中default可以放在任意位置case后必须是整型常量表达式
4、
switch中如果case后没有break,就继续执行代码
5、switch,case后必须是整型常量表达式
6、
7、
//三个整数从大到小输出 (低脂版 int main() { int a, b, c, ret; scanf("%d,%d,%d", &a, &b, &c); // if (a < b) { ret = a; a = b; b = ret; } if (a < c) { ret = a; a = c; c = ret; } if (b < c) { ret = c; b = c; c=ret; } printf("%d %d %d", a, b, c); return 0; } //三个整数从大到小输出(函数版 void swap(int*px, int*py) { int ret; ret = *px; *px = *py; *py = ret; } int main() { int a, b, c, ret; scanf("%d,%d,%d", &a, &b, &c); // if (a < b) { swap(&a, &b); } if (a < c) { swap(&a, &c); } if (b < c) { swap(&b, &c); } printf("%d %d %d", a, b, c); return 0; }
//生成1-100中三的倍数 int main() { for (int i = 0; i < 100; i++) { if (i % 3 == 0) printf("%d ", i); } return 0;
求两个数的最大公约数(暴力求解 //不够高效 int yueshu(int x, int y) { int min=(x>y) ? y : x; int m = min; if (x% m == 0&& y % m == 0) { return m; } else { for (int i = m; i>0; i--) { if(x% m == 0&& y % m == 0) return m; break; } } } int main() { int a, b; scanf("%d,%d", &a, &b); int m=yueshu(a, b); printf("%d", m); return 0; } //求最大公约数(辗转相除法 int main() { int a, b; scanf("%d,%d", &a, &b); while (a%b) { int c = a % b; a = b; b = c; } printf("%d", b); }
//最大公倍数 int main() { int a, b; scanf("%d,%d", &a, &b); int ret = a * b; while (a%b) { int c = a % b; a = b; b = c; } printf("%d", ret/b); }
1、while循环体
循环体执行次数比表达式执行次数少一次 要先进while()后的表达式
2、
3、
//编写代码数出1-100个数字中含有数字9的数 int main() { for (int i = 0; i < 100; i++) { if (i %10== 9) printf("%d ", i); if (i / 10 == 9) printf("%d ", i); if (i == 9 ) printf("%d ", i); } return 0; } 编写代码数出1-100个数字中含有数字9的数有多少 int main() { int count = 0; for (int i = 0; i < 100; i++) { if (i % 10 == 9) count++; if (i / 10 == 9) count++; //两个if都要判断 } printf("%d", count); return 0; }
// 求十个整数中最大值 int main() { //制造十个数 int arr[10] = { 0 }; //这里建议指定内容,如果不指定,会根据指定的内容进行元素个数 for (int j = 0; j < 10; j++) { scanf("%d", &arr[j]); } //找出最大值 int max=arr[0]; for (int i = 1; i < 10; i++) { if (arr[i] > max) max = arr[i]; } printf("%d", max); return 0; }
//输出99乘法口诀表 int main() { int i = 0; for (i = 1; i <=9; i++) { int j = 0; for (j = 1; j <=i; j++) printf("%d*%d=%2d ", i, j, i * j); //这里注意:左边补齐两位为-2d,右边补齐两位为2d printf("\n"); } return 0; }
//二分查找 int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int sz = (sizeof(arr) / sizeof(arr[0])); int x; int right = sz - 1; int left = 0; int mid = (left + right) / 2; scanf("%d", &x); while (left < right) { if (arr[mid] < x) { left = arr[mid]; mid = (left + right) / 2; } else if (arr[mid] > x) { right = arr[mid]; mid = (left + right) / 2; } else { arr[mid] = x; printf("找到啦!! 下标为%d!!!!", mid); break; } } }
//函数的习题讲解
1、形参是实参的临时拷贝
形参在函数调用的时候才实例化,才开辟内存空间
函数调用如果使用传值,改变形参不影响实参
2、
3、
函数设计高内聚低组合(函数设计相对单一独立
尽量少用全局变量
函数参数不要过多
设计函数时候建议谁申请的内存谁来释放
4、函数可以没有返回值
函数的实参可以是常量变量表达式
5、main函数的位置可以任意
6、
在不同函数中可以使用相同变量,相互不影响
函数的形参在栈(临时中保存
函数内定义的变量只能在函数内使用
栈区 | 局部变量 形式参数 返回值 |
---|---|
堆区 | 动态内存分布 malloc calloc realloc |
静态区 | 静态变量 全局变量 |
//实现一个函数,写乘法表,实现自己决定行列 void chengfa(int a) { int i = 0; for (i = 1; i <= a; i++) { int j = 0; for (j = 1; j <= i; j++) printf("%d*%d=%2d ", i, j, i * j); printf("\n"); } } int main() { int n=0; scanf("%d", &n); chengfa(n); return 0; }
//递归的作业
1、把函数处理结果的两个数据传给主函数:return两个数是做不到的哦
形参用数组
形参用两个指针
用两个全局变量
2、函数 可以传值调用传值的时候形参是实参的一份临时拷贝
函数可以传址调用,传址的时候可以通过形参改变实参的内容
函数不能嵌套定义但是可以嵌套调用
3、函数调用后不一定有返回值
实参和形参的名字可以同名
函数间数据传递可以使用全局变量
主函数和被调函数不一定会在同一个文件里