C 语言复习总结记录三

C 语言复习总结记录三

一 函数的定义

维基百科中对函数的定义:子程序

在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。

一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。

//函数的一般形式
ret_type fun_name(para1, * )
{
	statement; //语句项
}

ret_type  返回类型
fun_name  函数名
para1     函数参数
statement 函数体

二 库函数

为了支持可移植性和提高程序效率,C 语言基础库中提供了一系列的库函数,方便程序员进行软件开发。

学会查询工具的使用:

MSDN ( Microsoft Developer Network ) 索引 -> 输出查找

www.cplusplus.com 查阅所有库函数的原型和使用方法

http://en.cppreference.com(英文版)前面是 C++,后面是 C

http://zh.cppreference.com(中文版)

image-20241121145248523

C 语言常用的库函数有:

  1. IO 函数
  2. 字符串操作函数
  3. 字符操作函数
  4. 内存操作函数
  5. 时间/日期函数
  6. 数学函数

学习如何通过文档学习库函数,使用库函数,必须包含 #include 对应的头文件

strcpy

char source[] = "Hello World!";
char destination[20];
strcpy(destination, source);
printf("%s\n", destination);

memset

char str[] = "almost every programmer should know memset!";
memset(str, '-', 6);
puts(str);

三 自定义函数

自定义函数和库函数一样,有函数名,返回值类型和函数参数,但由程序员自己设计

编写函数,找出两个整数中的最大值;编写函数,交换两个整形变量的内容

#include<stdio.h>

int max(int x,int y) {
	return x > y ? x : y;
}

void swap(int* num1, int* num2) {	
	int tmp = *num1;
	*num1 = *num2;
	*num2 = tmp;
    
	*num1 = *num1 ^ *num2; //原理, 两次异或的结果等于原值
	*num2 = *num1 ^ *num2;
	*num1 = *num1 ^ *num2;
}


int main() {
	int num1 = 10, num2 = 5;
	printf("%d\n", max(num1, num2));

	printf("num1 = %d num2 = %d \n", num1, num2);
	swap(&num1, &num2);
	printf("num1 = %d num2 = %d \n", num1, num2);

	return 0;
}

四 函数的参数

4.1 实际参数(实参)

真实传给函数的参数,叫实参。

实参可以是:常量、变量、表达式、函数等。

无论实参何种类型,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。

4.2 形式参数(形参)

形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效

void swap(int* x, int* y) {	
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

int main() {
    int num1 = 10, num2 = 5;
	swap(&num1, &num2);
}

调试->窗口->监视,打开监视窗口

例,上述代码中的 x,y 是形式参数,而 &num1 和 &num2 是实参

image-20241121163838842

image-20241121163957531

这里可以看到 swap 函数在调用的时候, x , y 拥有自己的空间,同时拥有了和实参一模一样的内容。可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝

五 函数调用

5.1 传值调用

函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参

5.2 传址调用

传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量

函数的嵌套调用:函数和函数之间可以互相调用,但是不能嵌套定义

函数的链式访问:把一个函数的返回值作为另外一个函数的参数

#include <stdio.h>
#include <string.h>
int main()
{
    char arr[20] = "Hello";
	int ret = strlen(strcat(arr," World"));
	printf("%d\n", ret); //11 不包括末尾'\0'
	return 0;
}

#include <stdio.h>
int main()
{
    //int printf ( const char * format, ... );
    //On success, the total number of characters written is returned
    printf("%d", printf("%d", printf("%d", 43))); //4321
    return 0;
}
5.3 练习

1、编写函数判断一个数是不是素数

未定义标识符 bool, 引入 <stdbool.h> 头文件

/*
素数即质数,指一个大于 1 的自然数,除了 1 和它本身外,不能被其他自然数(质数)整除。通俗来说就是这个数除了 1 和它自身外再也没有其他因数。例如2,3,5,7 等都是素数,它们除了 1 和本身外再无其他因子。2 是最小的质数。

1 既不是质数也不是合数。合数是与质数相对的数,合数是指除了 1 和它本身外还有其他因数的数。例如 2,4,6,8 等皆是合数。
*/

//排除法
bool isPrimeNum(int num) {
	if (num <= 1) return false;
	else if (num == 2) return true; 
	
    //注意平方根的 <= 号
    //+= 2 这里是为了排除掉所有 2 的倍数
	for (int i = 3; i <= sqrt(num); i += 2)
		if (num % i == 0) return false;
	return true;
}

//筛选法, 资料参考 https://blog.csdn.net/m0_66769266/article/details/136145809
//找出 1000 以内的所有素数并打印
void isPrimeNum() {
	//bool 数组下标代表自然数, 值代表筛选标识 
    bool arr[MAX_NUM];
	memset(arr, 1, MAX_NUM * sizeof(bool));
    //将 0 , 1 排除, 该行可省略
	arr[0] = false, arr[1] = false;
	
    //遍历数组
    for (int i = 2;i < MAX_NUM; ++i) {
        //如果该数是素数, 则开始排除所有的倍数
		if (arr[i])
		{
            //i * i, 避免重复的排除动作
            // += i, 排除当前 i 的倍数, 减少循环次数
			for (int j = i * i; j < MAX_NUM; j += i)
				arr[j] = false;
		}
	}
	
    //遍历数组
	for (int i = 2;i < MAX_NUM;++i) {
		if (arr[i])
			printf("%d\n", i);
	}
}

2、编写函数判断一年是不是闰年

// 闰年, 是 4 的倍数但不是 100 的倍数; 是 400 的整数倍
void isLeapYear(int year) {
	if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0))
		printf("%d 该年份是闰年 \n", year);
	else
		printf("%d 该年份不是闰年 \n", year);
}

3、编写函数,实现一个整形有序数组的二分查找

int binarySearch(int* arr, int len, int key) {
	int index = -1, left = 0, right = len;
	while (left <= right) {
		index = (left + right) / 2;
		if (key > arr[index])
			left = index + 1;
		else if (key < arr[index])
			right = index - 1;
		else
			return index;
	}
	return -1;
}

4、编写函数,每调用一次这个函数,就会将 num 的值增加 1

void addNum(int* x)
{
	++*x;
}

int main()
{
     int num = 0;
     addNum(&num); //调用函数,使得 num 每次增加 1
     return 0;
}

6 函数的声明和定义

函数声明:仅提供函数名,参数和返回类型,不提供参数体,一般放在头文件中

函数定义:指函数的具体实现,交待函数的功能实现

#ifndef __TEST_H__
#define __TEST_H__
int Add(int x, int y); //函数声明
#endif //__TEST_H__

#include "test.h"
int Add(int x, int y) //函数实现
{
 return x+y;
}

7 函数递归

7.1 递归定义

递归 :程序调用自身的编程技巧称为递归(recursion)

递归作为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

递归的两个必要条件 ① 存在限制条件,当满足这个限制条件的时候,递归便不再继续 ② 每次递归调用之后越来越接近这个限制条件

递归的主要思考方式在于:把大事化小

7.2 递归练习

接受一个整型值(无符号),按照顺序打印它的每一位

例如:输入:1234,输出 1 2 3 4

void printNum(unsigned int num) {
	if (num > 9) 
		printNum(num / 10);
	printf("%u ", num % 10);
}

编写函数不允许创建临时变量,求字符串的长度

int strLen(char* str) {
	if (*str == '\0') //因为没办法利用下标, 所以利用解引用 * 取到值
		return 0;
	else
		return 1 + strLen(str + 1); //利用指针偏移 移动
}
7.3 递归与迭代

求 n 的阶乘(不考虑溢出)

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

求第 n 个斐波那契数(不考虑溢出)

//斐波那契数是一种整数序列,其每个数是前两个数的和
int fabonacciNum(int n) {
	if (n <= 2) return 1;
	else return fabonacciNum(n - 1) + fabonacciNum(n - 2);
}

在调试 fabonacciNum 时,如果参数比较大,那就会报错: stack overflow(栈溢出)ERROR。因为系统分配给程序的栈空间是有限的,如果出现了死循环,或死递归,这会导致栈空间一直在开辟,最终空间耗尽,该现象称为栈溢出

如何解决递归函数的调用层级很深的问题 :

1、将递归改写成非递归

2、使用 static 对象替代 nonstatic 局部对象,既减少每次递归调用和返回时产生和释放 nonstatic 对象的开销,而且 static 对象还可以保存递归调用的中间状态,为各个调用层所访问

非递归方式实现

int factorial(int n) {
	int res = 1;
	for (int i = 1; i <= n; ++i) {
		res *= i;
	}
    
    /* while (n > 1)
    {
        result *= n ;
        n -= 1;
    } */
    
	return res;
}

int fabonacciNum(int n) {
	int fib_n = 1, fib_n_minus_one = 1, fib_n_minus_two = 1;
    for (int i = 3; i <= n; ++i)
    {
        fib_n = fib_n_minus_one + fib_n_minus_two;
        fib_n_minus_two = fib_n_minus_one;
        fib_n_minus_one = fib_n;
    }

    /*int fib_n = 1, fib_n_minus_one = 1, fib_n_minus_two = 1;
    while (n > 2)
    {
        fib_n = fib_n_minus_one + fib_n_minus_two;
        fib_n_minus_two = fib_n_minus_one;
        fib_n_minus_one = fib_n;
        --n;
    }*/

	return fib_n;
}

注 :许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。但这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销

函数递归经典题目 :

1、汉诺塔问题

/*
一块板上有三根柱子 A、B、C。A 柱上套有 n 个大小不等的圆盘,按照大的在下、小的在上的顺序排列,要把这 n 个圆盘从 A 柱移动到 C 柱上,每次只能移动一个圆盘,移动过程可以借助 B 柱。但在任何时候,任何柱上的圆盘都必须保持大盘在下,小盘在上

从键盘输入需移动的圆盘个数,给出移动的过程
*/
//参考资料 https://blog.csdn.net/lllsure/article/details/135913834
//汉诺塔一样也是找一个 式子,不过这个式子是抽象的,可以理解成一个关键的步骤 :一共 n 个圆盘,把 (n-1) 个圆盘从 A 移到 B,在把最大的圆盘从 A 移到 C,最后把 B 上的所有圆盘从 B 移到 C 。就是这么一个过程

#include<stdio.h>
//用于打印的函数
void move(char pos1, char pos2) {
    printf("%c -> %c", pos1, pos2);
}

void hanoi(int n, char pos1, char pos2, char pos3) {
    if (n == 1)
    {
     	move(pos1, pos3);   
    }
    else
    {
        hanoi(n - 1, pos1, pos3, pos2); //从 A 柱移动到 B 柱
        move(pos1, pos3); //打印最大的判断从 A -> C
        hanoi(n - 1, pos2, pos1, pos3); //从 B 柱移动到 C 柱
    }
}

int main() {
    int n;
    scanf("%d", &n);
    hanoi(n, 'A', 'B', 'C');
}

2、青蛙跳台阶

//青蛙跳台阶,一次一个台阶或者一次跳两个台阶,问跳 n 个台阶有多少次跳法
int jumpStairs(int n) {
	/*if (n == 1) return 1;
	else if (n == 2) return 2;
	else return jumpStairs(n - 1) + jumpStairs(n - 2);*/

	int res = 1, res_minus_one = 1,res_minus_two = 1;
	while (n >= 2) {
		res = res_minus_one + res_minus_two;
		res_minus_two = res_minus_one;
		res_minus_one = res;
		--n;
	}
	return res;
}
t n;
    scanf("%d", &n);
    hanoi(n, 'A', 'B', 'C');
}

2、青蛙跳台阶

//青蛙跳台阶,一次一个台阶或者一次跳两个台阶,问跳 n 个台阶有多少次跳法
int jumpStairs(int n) {
	/*if (n == 1) return 1;
	else if (n == 2) return 2;
	else return jumpStairs(n - 1) + jumpStairs(n - 2);*/

	int res = 1, res_minus_one = 1,res_minus_two = 1;
	while (n >= 2) {
		res = res_minus_one + res_minus_two;
		res_minus_two = res_minus_one;
		res_minus_one = res;
		--n;
	}
	return res;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/921278.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL——多表设计以及数据库中三种关系模型

大致介绍数据库中三种关系模型 一对多&#xff08;1:N&#xff09; 定义&#xff1a; 一个实体可以与另一个实体的多个实例相关联&#xff0c;而后者只能与前者的一个实例相关联。 例子&#xff1a; 学生和课程的关系。 学生&#xff08;1&#xff09;&#xff1a;每个学生…

OpenCV和Qt坐标系不一致问题

“ OpenCV和QT坐标系导致绘图精度下降问题。” OpenCV和Qt常用的坐标系都是笛卡尔坐标系&#xff0c;但是细微处有些不同。 01 — OpenCV坐标系 OpenCV是图像处理库&#xff0c;是以图像像素为一个坐标位置&#xff0c;即一个像素对应一个坐标&#xff0c;所以其坐标系也叫图像…

实验四:构建园区网(OSPF 动态路由)

目录 一、实验简介 二、实验目的 三、实验需求 四、实验拓扑 五、实验步骤 1、在 eNSP 中部署网络 2、设计全网 IP 地址 3、配置二层交换机 4、配置路由交换机并测试通信 5、配置路由接口地址 6、配置 OSPF 动态路由&#xff0c;实现全网互通 一、实验简介 使用路由…

《剖析 Spring 原理:深入源码的旅程(二)》

六、Spring 的 Bean 注入与装配 Spring 的 Bean 注入与装配的方式有很多种&#xff0c;可以通过 xml、get set 方式、构造函数或者注解等。简单易用的方式就是使用 Spring 的注解&#xff0c;Spring 提供了大量的注解方式&#xff0c;如 Autowired、Qualifier 等。Spring 还支持…

Java文件上传解压

目录结构 工具类 枚举 定义文件类型 public enum FileType {// 未知UNKNOWN,// 压缩文件ZIP, RAR, _7Z, TAR, GZ, TAR_GZ, BZ2, TAR_BZ2,// 位图文件BMP, PNG, JPG, JPEG,// 矢量图文件SVG,// 影音文件AVI, MP4, MP3, AAR, OGG, WAV, WAVE}为了避免文件被修改后缀&#xff0…

CSRF保护--laravel进阶篇

laravel对csrf非常重视&#xff0c;专门针对csrf作出了很多的保护。如果您是刚刚接触laravel的路由不久&#xff0c;那么您可能对于web.php路由文件的post请求很疑惑&#xff0c;因为get请求很顺利&#xff0c;而post请求则可能会遭遇失败。其中一个失败的原因是由于laravel的c…

jupyter notebook的 markdown相关技巧

目录 1 先选择为markdown类型 2 开关技巧 2.1 运行markdown 2.2 退出markdown显示效果 2.3 注意点&#xff1a;一定要 先选择为markdown类型 3 一些设置技巧 3.1 数学公式 3.2 制表 3.3 目录和列表 3.4 设置各种字体效果&#xff1a;加粗&#xff0c;斜体&#x…

【GAT】 代码详解 (1) 运行方法【pytorch】可运行版本

GRAPH ATTENTION NETWORKS 代码详解 前言0.引言1. 环境配置2. 代码的运行2.1 报错处理2.2 运行结果展示 3.总结 前言 在前文中&#xff0c;我们已经深入探讨了图卷积神经网络和图注意力网络的理论基础。还没看的同学点这里补习下。接下来&#xff0c;将开启一个新的阶段&#…

redis工程实战介绍(含面试题)

文章目录 redis单线程VS多线程面试题**redis是多线程还是单线程,为什么是单线程****聊聊redis的多线程特性和IO多路复用****io多路复用模型****redis如此快的原因** BigKey大批量插入数据测试数据key面试题海量数据里查询某一固定前缀的key如果生产上限值keys * &#xff0c;fl…

神经网络问题之二:梯度爆炸(Gradient Explosion)

梯度爆炸&#xff08;Gradient Explosion&#xff09;是神经网络训练过程中常见的一个问题&#xff0c;它指的是在反向传播过程中&#xff0c;梯度值变得非常大&#xff0c;超出了网络的处理范围&#xff0c;从而导致权重更新变得不稳定甚至不收敛的现象。 一、产生原因 梯度爆…

小杨的N字矩阵c++

题目描述 小杨想要构造一个m*m 的 N 字矩阵&#xff08; m为奇数&#xff09;&#xff0c;这个矩阵的从左上角到右下角的对角线、第1 列和第m 列都 是半角加号 &#xff0c;其余都是半角减号 - 。例如&#xff0c;一个 5*5 的 N 字矩阵如下&#xff1a; --- -- -- -- --- 请…

2024 年企业中的生成式 AI 现状

2024: The State of Generative AI in the Enterprise - Menlo Ventures 企业 AI 格局正在被实时改写。随着试点&#xff08;Pilot&#xff09;让位于生产&#xff08;Production&#xff09;&#xff0c;我们对 600 名美国企业 IT 决策者进行了调查&#xff0c;以揭示新出现的…

Ubuntu24虚拟机-gnome-boxes

推荐使用gnome-boxes&#xff0c; virtualbox构建失败&#xff0c;multipass需要开启防火墙 sudo apt install gnome-boxes创建完毕&#xff5e;

Haystack 的开源开发 LLM 应用设计框架

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

leetcode 919.完全二叉树插入器

1.题目要求: 完全二叉树 是每一层&#xff08;除最后一层外&#xff09;都是完全填充&#xff08;即&#xff0c;节点数达到最大&#xff09;的&#xff0c;并且所有的节点都尽可能地集中在左侧。设计一种算法&#xff0c;将一个新节点插入到一棵完全二叉树中&#xff0c;并在…

提升性能测试效率与准确性:深入解析JMeter中的各类定时器

在软件性能测试领域&#xff0c;Apache JMeter是一款广泛使用的开源工具&#xff0c;它允许开发者模拟大量用户对应用程序进行并发访问&#xff0c;从而评估系统的性能和稳定性。在进行性能测试时&#xff0c;合理地设置请求之间的延迟时间对于模拟真实用户行为、避免服务器过载…

Python + 深度学习从 0 到 1(00 / 99)

希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【深度学习从 0 到 1】谢谢你的支持&#xff01; ⭐ 什么是深度学习&#xff1f; 人工智能、机器学习与…

亚信安全发布《2024年第三季度网络安全威胁报告》

《亚信安全2024年第三季度网络安全威胁报告》的发布旨在从一个全面的视角解析当前的网络安全威胁环境。此报告通过详尽梳理和总结2024年第三季度的网络攻击威胁&#xff0c;目的是提供一个准确和直观的终端威胁感知。帮助用户更好地识别网络安全风险&#xff0c;并采取有效的防…

ROS机器视觉入门:从基础到人脸识别与目标检测

前言 从本文开始&#xff0c;我们将开始学习ROS机器视觉处理&#xff0c;刚开始先学习一部分外围的知识&#xff0c;为后续的人脸识别、目标跟踪和YOLOV5目标检测做准备工作。我采用的笔记本是联想拯救者游戏本&#xff0c;系统采用Ubuntu20.04&#xff0c;ROS采用noetic。 颜…

利用c语言详细介绍下选择排序

选择排序&#xff08;Selection sort&#xff09;是一种简单直观的排序算法。它是每次选出最小或者最大的元素放在开头或者结尾位置&#xff08;采用升序的方式&#xff09;&#xff0c;最终完成列表排序的算法。 一、图文介绍 我们还是使用数组【10&#xff0c;5&#xff0c;3…