函数
函数的传值调用
函数的形参和实参占用不同的代码块,对形参的修改并不会修改实参的值
函数的传址调用
传址调用指的是将函数外部创建的内存地址传给函数形参的一种调用方式
这种传参方式可以在函数内部操作函数外部的变量
练习
二分查找
#include<stdio.h>
int binary_2(int arr[] , int k,int len);
int main() {
int arr[ ] = { 1,2,3,4,5,6,7,8,9,10 };
int len = sizeof(arr) / sizeof(arr[0]);
int element ;
printf("请输入你想要查找的元素");
scanf("%d", &element);
int ret;
ret = binary_2(arr ,element,len);
if (ret == -1) {
printf("没有找到指定元素\n");
}
else {
printf("找到了,元素的下标是%d\n",ret);
}
return 0;
}
int binary_2(int arr[] , int k,int len) {
int left = 0;
int right = len - 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;
}
注意点:
1)一个是函数的len要在main函数内部进行计算,因为如果在函数内部计算,size(arr) 的值实际上是4 , 为什么呢,因为传入的是一个指针变量,指向的是第一个元素的地址,算出来的大小就是int* 大小
#include<stdio.h>
int main() {
素的下标是%d\n",ret);
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr[0]));
return 0;
}
2)第二个要注意的是,传入的形参int arr[] 为什么是指针变量呢 , int arr[] 等价于 int * arr,两者是一样的
函数的模块化使用
从一个源文件A到另一个源文件B中 ,想要在源文件A中使用源文件B中的函数 ,
1)首先要在源文件A中加入 #include “ 你想要使用的函数名”
- 在头文件中新建一个和B一样名字的头文件(.h)结尾
举一个例子:
源文件A,名字是str.c :
#include<stdio.h>
#include"add.h"
int main2() {
int x = 1, y = 4;
int sum = Add(x, y);
printf("%d", sum);
return 0;
}
源文件B,名字是add.c :
#include<stdio.h>
int Add(int x, int y) {
return x + y;
}
要主动添加的头文件C,名字是add.h(这个一般和头文件相同) :
#ifndef __ADD_H_
#define __ADD_H_
int Add(int x, int y);
#endif
这个是格式,理解一下:第一行发现没有定义的话,如果定义过的话,就不用定义了,避免了重复定义
函数的递归
什么是递归
函数调用自身的一种编程技巧(recursion)
主要的思维:化大为小,一层一层的拨
我觉得可以想象成一个倒立的金字塔,最底下一层是开放的,上面几层都有权限不能开放,递归的过程就是向上开锁的过程
练习1:
编写函数逐个打印出数字“1234”中的 1 , 2, 3,4
#include<stdio.h>
void print(int i) {
if (i > 9)
{
print(i/10);
}
printf("%d\n", i%10);
}
int main() {
unsigned int i = 1234;
print(i);
return 0;
}
解题思路:
由大化小,设置权限,层层递推!
练习2:
通过自己写的my_strlen起到strlen的作用,输出字符串的个数,用两种方式
提示:一种是创建临时变量,一种不创建
#include<stdio.h>
int my_strlen1(char * );
int my_strlen2(char *);
int main() {
char arr[] = "superzkx";
int len1 = my_strlen1(arr);
int len2 = my_strlen2(arr);
printf("%d\n", len1);
printf("%d\n", len2);
return 0;
}
int my_strlen1(char* arr) {
int coust = 0;
while (*arr != '\0') {
*arr++;
coust++;
}
return coust;
}
int my_strlen2(char* arr) {
if (*arr != '\0') {
return 1 + my_strlen2(arr + 1);
}
return 0;
}
由此可见,递归将原来的while换成了if,其中的return 其实起到的是while的作用
练习3:
函数计算n!的值
#include<stdio.h>
int main() {
int n;
int sum1, sum2;
printf("请输入要计算阶乘的数");
scanf("%d", &n);
sum1 = factorial_1(n);
sum2 = factorial_2(n);
printf("sum1==%d,sum2==%d", sum1, sum2);
return 0;
}
int factorial_1(int n) {
int i;
int sum = 1;
for (i = 1; i <= n; i++) {
sum = sum * i;
}
return sum;
}
int factorial_2(int n) {
if (n == 1)
return 1;
else if (n > 1) {
return n * factorial_2(n - 1);
}
}
可以看出 循环 和 递归 其实是可以互相表示的
练习4:
计算斐波那契数列(不算栈溢出)
#include<stdio.h>
int Fibo1(int n);
int Fibo2(int n);
int main() {
int n;
int sum1, sum2;
printf("请输入要计算Fibo的数");
scanf("%d", &n);
sum1 = Fibo1(n);
sum2 = Fibo2(n);
printf("sum1== %d,sum2== %d", sum1, sum2);
return 0;
}
int Fibo1(int n) {
if (n == 1)
return 1;
else if (n == 2)
return 1;
else
return Fibo1(n - 1) + Fibo2(n - 2);
}
int Fibo2(int n) {
int a = 1;
int b = 1;
int c = 0;
if (n == 1) {
return a;
}
if (n == 2) {
return b;
}
while (n > 2) {
c = a + b;
a = b;
b = c;
n--;
}
return c;
}
经典递归问题:
汉诺塔
#include<stdio.h>
void Hanoi(int n, char A, char B, char C);
int main() {
int n =0;
char A = 'A';
char B = 'B';
char C = 'C';
printf("请输入汉诺塔的层数(目标是将A柱移动到C柱)\n");
scanf("%d", &n);
Hanoi(n,A,B,C);
return 0;
}
void Hanoi(int n,char A, char B ,char C ) {
if (n == 1) {
printf("将%c-->%c\n",A,C);
}
else {
Hanoi(n - 1, A, C, B);
printf("%c-->%c\n",A,C);
Hanoi(n - 1, B, A, C);
}
}
主要思想:n>=2时(n>=3)时更明显,把前n-1个甜甜圈看成一个整体,实际上重复的就是n=2时候的操作
就是!
将前n-1个 从 A 通过C 全部移到B
然后A最后一个大的到C
最后B通过A到C
简简单单 KO!
|