C语言从入门到精通——初阶3 函数和循环

 42 函数讲解

库函数 

库函数集合网址:cplusplus.com - The C++ Resources Network

学会看文档查找学习库函数的使用

C/C++官网:cppreference.com

自定义函数 

函数返回类型的地方写:void,表示这个函数不返回任何值,也不需要返回

函数的参数

实参:实际参数简称“实参”。在调用有参函数时,函数名后面括号中的参数称为“实际参数”,实参可以是常量、变量或表达式。

形参:自定义函数中的“形参”全称为"形式参数" 由于它不是实际存在变量,所以又称虚拟变量实参和形参可以重名。是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。

当实参传递给形参时,形参其实是实参的一份临时拷贝,对形参的修改是不会改变实参的。

45 函数的调用 

传值调用:函数的形参和实参分别占有不同的代码块,对形参的修改不会影响实参。

传址调用:把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式,函数内部可以直接操作函数外部的变量。

//判断素数函数(打印100-200之间的素数)


#include<math.h>

int is_prime(int n)
{
	int i = 0;
	for (i = 2; i <= sqrt(n); i++) {
		if (n % i == 0)
			return 0;
	}
	return 1;

}

int main()
{
	int i = 0;
	for (i = 1; i <= 200; i++)
	{
		if (is_prime(i) == 1)
			printf("%d ", i);
	}
	return 0;
}

函数的功能单一足够独立比较好,可用性强 

//判断闰年函数

int isaddyear(int year)
{
	if (year % 100!=0&&year % 4 == 0)
		return 1;
	else if (year % 400 == 0)
		return 1;
	else
		return 0;
 }
 
int main()
{
	int year = 0;
	for (year = 1000; year <= 2000; year++)
	{
		if (isaddyear(year) == 1)
		{
			printf("%d ", year);
		}
	}
	return 0;
}

 //二分查找函数

//本质上arr是一个指针
int binary_search(int arr[], int k,int sz)//这里的arr不是数组,传递过去的是arr首元素的地址,本质上是个指针
{
	//int sz = sizeof(arr) / sizeof(arr[0]);不能在这里求数组个数
	int left = 0;
	int right = sz;

	while (left <= right)
	{
		int mid = (left + right) / 2;//不能放到循环外
		if (arr[mid]> k)
		{
			right = mid-1;
		}
		else if (arr[mid] < k)
		{
			left = mid + 1;
		}
		else
			return mid;
	}
		return -1;


}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	int k = 17;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//arr传递过去的是数组首元素地址
	int ret=binary_search(arr, k,sz);
	if (ret == -1)
	{
		printf("找不到\n");
	}
	else
		printf("找到了,数组下标为%d", ret);
	return 0;
}

 如果函数内部需要参数部分传入某个数组元素的个数,一定是在外部求好传入的,函数内部无法求出数组元素个数

//函数每次调用sum自加1

void Add(int* p)
{
	(*p)++;
}
int main()
{
	int sum = 0;
	Add(&sum);
	printf("%d\n", sum);
	Add(&sum);
	printf("%d\n", sum);
	Add(&sum);
	printf("%d\n", sum);

}

47 函数的嵌套调用和链式访问

  • 函数的嵌套调用:函数和函数之间可以有机的组合
  • 函数的链式访问:把一个函数的返回值作为另外一个函数的参数

​ 函数的声明和定义

48 函数递归

函数递归:程序调用自身

一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法

主要思考方式:大事化小

  • 递归容易栈溢出

递归的两个必要条件:

  • 存在限制条件,满足后不在继续
  • 每次递归调用后越来越接近这个限制条件

栈区:局部变量、函数形参

堆区:动态开辟的内存(malloc,calloc等)

静态区:全局变量,static修饰的变量

//接受一个无符号整型值,按照顺序打印它的每一位。(例如输入:123,输出:1 2 3)

void print(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;
}

 //模拟实现strlen函数

int my_strlen(char* str)
{
	int count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

int main()
{
	char arr[] = "skyesun";
	int len = my_strlen(arr);//arr是数组,数组传参,传过去的不是整个数组,而是第一个元素的地址
	printf("len=%d\n", len);

	return 0;
}

//要求不创建临时变量:递归的办法(编写函数不允许创建临时变量,求字符串的长度)

int my_strlen(char* str)
{
	if (*str != '\0')
		return 1 + my_strlen(str + 1);
	else return 0;
}

递归与迭代 

递归和迭代的区别:

一、含义不同:

递归是重复调用函数自身实现循环迭代是函数内某段代码实现循环,循环代码中参与运算的变量同时是保存结果的变量,当前保存的结果作为下一次循环计算的初始值

递归循环中,遇到满足终止条件的情况时逐层返回来结束。迭代则使用计数器结束循环。当然很多情况都是多种循环混合采用,这要根据具体需求。

二、结构不同:

递归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构。 递归与迭代都涉及重复:迭代显式使用重复结构,而递归通过重复函数调用实现重复。

递归与迭代都涉及终止测试:迭代在循环条件失败时终止,递归在遇到基本情况时终止,使用计数器控制重复的迭代和递归都逐渐到达终止点:迭代一直修改计数器,直到计数器值使循环条件失败;递归不断产生最初问题的简化副本,直到达到基本情况。

//递归求n的阶乘 

int factorial(int n)
{
	if (n <= 1)
		return 1;
	else
		return n * factorial(n - 1);
}

//求第n个斐波那契数

int fib(int n)
{
	if (n <= 2)
		return 1;
	else
		return fib(n - 1) + fib(n - 2);//会有重复大量的计算,效率低
}

//用循环解决效率高

int Fib(int n)
{
	int a = 1;
	int b = 1;
	int c = 1;

	while (n>2)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}

课后思考题

1.汉诺塔问题

汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

当数量为3时的动态演示:

算法分析:递归

第一次移动,要把A柱子上的前n-1个移动到B柱子上;(图1)
第二次移动,直接把A柱子上的最后一个移动到C柱子上;(图2)
第三次移动,把B柱子上的n-1个柱子通过柱子A移动到柱子C上。(图3)

代码实现:

void Hanoi(int n, char a, char b, char c);
void Move(int n, char a, char b);
int count;
int main()
{
    int n = 8;
    printf("The number of floors of the Tower of Hanoi:\n");
    scanf(" %d", &n);
    Hanoi(n, 'A', 'B', 'C');
    return 0;
}
void Hanoi(int n, char a, char b, char c)
{
    if (n == 1)
    {
        Move(n, a, c);
    }
    else
    {
        Hanoi(n - 1, a, c, b);//把A柱子上的前n-1个移动到B柱子上
        Move(n, a, c);//把A柱子上的最后一个移动到C柱子上
        Hanoi(n - 1, b, a, c);//把B柱子上的n-1个移动到柱子C上
    }
}
void Move(int n, char a, char b)
{
    count++;
    printf("%d step Move %d: Move from %c to %c !\n", count, n, a, b);
}

2.青蛙跳台阶问题

 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
    如果n=1,只有一种跳法,那就是1
    如果n=2,那么有两种跳法,2,[1,1]
    如果n=3,那么有三种跳法,[1,1,1],,[1,2],[2,1]
    如果n=4,那么有五种跳法,[1,1,1,1],[1,1,2],[1,2,1],[2,1,1],[2,2]
    如果n=5,那么有八种跳法,[1,1,1,1,1],[1,1,1,2],[1,1,2,1],[1,2,1,1],[2,1,1,1],[2,2,1],[2,1,2],[1,2,2]
    结果为1,2,3,5,8  ,是斐波那切数列
    递归做法:

    public static int jump(int n){
        if (n==0)
            return 0;
        if (n==1)
            return 1;
        if (n==2)
            return 2;
        return jump(n-1)+jump(n-2);
    }

非递归做法:

public static int jump2(int n){
        if (n==0)
            return 0;
        if (n==1)
            return 1;
        if (n==2)
            return 2;
        int n1=1;
        int n2=2;
        int count=2;
        while (count++<=n){
            int tmp=n1;
            n1=n2;
            n2=tmp+n2;
        }
        return n2;
}

思考题参考链接:

汉诺塔递归调用(C语言实现)_一禅的师兄的博客-CSDN博客_汉诺塔c语言

汉诺塔(益智玩具)_百度百科 (baidu.com)

算法—青蛙跳台阶问题汇总_zhangshk_的博客-CSDN博客_青蛙跳台阶

53 函数作业讲解

 //字符串逆序(递归实现)编写一个函数reserve_string(char * string)(递归实现)

int my_strlen(char* str)
{
	int count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}

	return count;
}

//void reverse_string(char* str)
//{
//	int left = 0;
//	int right = my_strlen(str)-1;
//
//	while (left<right)
//	{
//		char tmp = str[left];
//		str[left] = str[right];
//		str[right] = tmp;
//		left++;
//		right--;
//	}
//}

//void reverse_string(char* str)
//{
//	int left = 0;
//	int right = my_strlen(str) - 1;
//
//	while (left < right)
//	{
//		char tmp = *(str + left);//str[left];
//		*(str + left) = *(str+right);
//		*(str+right) = tmp;
//		left++;
//		right--;
//	}
//}

void reverse_string(char* str)
{
	char tmp = *str;//1
	int len = my_strlen(str);
	*str = *(str + len - 1);//2
	*(str + len - 1) = '\0';//3
	//判断条件
	if(my_strlen(str+1)>=2)
	{
		reverse_string(str + 1);//4
	}

	*(str + len - 1) = tmp;//5
}
int main()
{
	char arr[] = "abcdef";
	reverse_string(arr);//数组名arr是数组arr首元素的地址

	printf("%s\n", arr);//fedcba
	return 0;
}

 //计算一个数的每位之和(递归实现)

int DigitSum(int n)
{
	if (n > 9)
	{
		return DigitSum(n / 10) + n % 10;
	}
	else
	{
		return n;
	}
}

//编写一个函数实现n的k次方,使用递归实现 

double Pow(int n, int k)
{
	if (k == 0)
		return 1.0;
	else if (k > 0)
		return n * Pow(n, k - 1);
	else
		return 1.0 / (Pow(n, -k));
}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程内容100+集 C 语言编程实战课程,由 WangTeacher 主讲 。大神手把手教你编程,精心制作一部视频教学实战课程,细致到开发环境的搭建,都是手把手传授。并结合 15 年的编程经验,把最先进的软件开发流程和编程技巧融合到课程中,代码示例涵盖了 C 语言的每一个知识点,你值得拥有!第一章 基础技能篇:C 语言简介,基础语法和规则,数据类型,变量,运算符,流程控制,数组,字符串,函数,结构体,联合体,指针,文件操作,代码调试等知识点。第二章 实战技能篇:编程规范、内存管理、Makefile、进程内存布局、动态库、静态库、链表、排序算法、C 函数库介绍、递归函数、多进程调试等适用技能。第三章 案例分享篇:学员管理系统,从需求设计,架构设计,到模块设计,再到各个模块编码,学会从专业软件开发的角度去理解编程,他确实就是一门艺术。第四章 面试题篇:所有付出了时间和精力的学习,都是为了有回报,学习计算机技术更是如此,通过分析常见面试题,可以帮助你查漏补缺,更快、更容易实现回报。第五章 补充课程:在学员学习的过程中如果出现一个疑问,或者不够深入讲解的地方进行补充的课程。  课程特色案例:从专业程序员的角度思考问题和编写代码。实用:课程中融合了多年总结的编程技巧。快速:最快掌握编程的方法就是模仿和刻意练习。方式:脱离实践的传道都是扯蛋,能用代码演示的地方,绝不废话,每课都是编码,以最接近专业程序员日常的方式,手把手带你入行,和大神结对编程就是高效。 适合人群小学中学生,专科本科生,就业想转行生;逻辑思维差,空间想象差,编程可以改善;平时爱唱歌,却五音不全,写程序很合适;想尽快挣钱,迎娶白富美,达到人生巅峰。 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值