Linux C编程一站式学习笔记5

news2024/11/15 14:01:27

Linux C编程一站式学习笔记 chap5 深入理解函数

文章目录

  • Linux C编程一站式学习笔记 chap5 深入理解函数
  • 一.return语句
    • 习题
  • 二.增量式开发
  • 三.递归
    • 我猜有递归可视化工具,一搜果真有收获
    • 习题
      • GCD(Greatest Common Divisor) 最大公约数
      • Fibonacci
  • 相关资源、参考资料

嘶,大一时的C语言学的时候确实没有总结过这个return,当时也木有学那个增量式开发

一.return语句

  • 在有返回值的函数中,return语句的作用是提供整个函数的返回值,并结束当前函数返回到调用它的地方

  • 在没有返回值的函数中也可以使用return语句,例如当检查到一个错误时提前结束当前函数的执行并返回

    #include <math.h>
    
    void print_logarithm(double x)
    {
    	if (x <= 0.0) {
    		printf("Positive numbers only, please.\n");
    		return;
    	}
    	printf("The log of x is %f", log(x));
    }
    

    这个函数首先检查参数x是否大于0,如果x不大于0就打印错误提示,然后提前结束函数的执行返回到调用者,只有当x大于0时才能求对数,在打印了对数结果之后到达函数体的末尾,自然地结束执行并返回。注意,使用数学函数log需要包含头文件math.h,由于x是浮点数,应该与同类型的数做比较,所以写成0.0

  • 逻辑与运算符在C语言中写成两个&号(Ampersand)

  • C语言还提供了逻辑或(Logical OR)运算符,写成两个|线(Pipe Sign)

  • 逻辑非(Logical NOT)运算符,写成一个!号(Exclamation Mark)

  • 在编程语言中表示真和假的数据类型叫做布尔类型,在C语言中通常用int型来表示,非0表示真,0表示假

  • 例:返回布尔值的检查奇偶性的函数

    int is_even(int x)
    {
    	if( x%2 == 0)
    			return 1;
    	else
    			return 0;
    }
    

    写成return(1);这种形式也可以,表达式外面套括号表示改变运算符优先级,在这里不起作用

    返回布尔值的函数是一类非常有用的函数,在程序中通常充当控制表达式,函数名通常带有isif等表示判断的词,这类函数也叫做谓词(Predicate)

    我们可以调用这个函数,比如下面这种写法

    int i = 19;
    if (is_even(i)) {
    	/* do something */
    } else {
    	/* do some other thing */
    }
    
  • 我们可以简化一下刚刚那个 is_even 函数,x % 2这个表达式本来就有0值或非0值,直接把这个值当作布尔值返回就可以了

    int is_even(int x)
    {
        return !( x % 2);
    }
    
  • 函数的返回值可以这样理解:

    函数返回一个值相当于定义一个和返回值类型相同的临时变量,并用return后面的表达式来初始化

    例如上面的函数调用相当于这样的过程:

    int 临时变量 = !(x % 2); 
    函数退出,局部变量x的存储空间释放; 
    if (临时变量) { /* 临时变量用完就释放 */ 
        /* do something */ 
    } else { 
        /* do some other thing */ 
    }
    

    if语句对函数的返回值做判断时,函数已经退出,局部变量x已经释放,所以不可能在这时候才计算表达式!(x % 2)的值,表达式的值必然是事先计算好了存在一个临时变量里的,然后函数退出,局部变量释放,if语句对这个临时变量的值做判断。

  • 注意,虽然函数的返回值可以看作是一个临时变量,但我们只是读一下它的值,读完值就释放它,而不能往它里面存新的值,换句话说,函数的返回值不是左值,或者说函数调用表达式不能做左值,因此下面的赋值语句是非法的:is_even(20) = 1; xxx

    返回值也是按值传递(call by value)的,即便返回语句写成return x;,返回的也是变量x的值,而非变量x本身,因为变量x马上就要被释放了。

习题

1、编写一个布尔函数int is_leap_year(int year),判断参数year是不是闰年。如果某年份能被4整除,但不能被100整除,那么这一年就是闰年,此外,能被400整除的年份也是闰年。

int is_leap_year(int year)
{
	if( year % 4 == 0 && year % 100 !=0 || year % 400 == 0)
		return 1;
	else
		return 0;
}

2、编写一个函数double myround(double x),输入一个小数,将它四舍五入。例如myround(-3.51)的值是-4.0,myround(4.49)的值是4.0。可以调用math.h中的库函数ceilfloor实现这个函数。

  • 一开始写的

    double myround(double x)
    {
        if( x * 100 % 100 >= 50)
        {
            return ceil(x);
        }
        else
        {
            return floor(x);
        }
    }
    

    vscode 和 pythontutor都报了这个错误,看来有点想当然了…

    image-20230122231337519

    image-20230122231143302
  • 这个应该是可以的👇

    #include <math.h>
    double myround(double x) {
        if (x >= 0) {
            return floor(x + 0.5);
        } else {
            return ceil(x - 0.5);
        }
    }
    

二.增量式开发

这节主要讲的是一种编程思维吧

  • 这一节讲的太棒了!
  • 值得反复回看!!!
    • http://akaedu.github.io/book/ch05s02.html
    • 从初学者的视角以一个简单的例子引入,命中了很多痛点,让人读完恍然大悟, 很多曾经试过的方法作为一时的经验已经遗忘了, 但读完这节发现那些方法是可以进入思维体系的!
    • 此外,这个对于写博客也很有启发,真的写的很通俗易懂且有趣,最重要的是有代入感!

三.递归

  • 如果定义一个概念需要用到这个概念本身,我们称它的定义是递归的(Recursive)

  • 数学上的阶乘(factorial)就是用它自己来定义的:n的阶乘等于n乘以n-1的阶乘

    • n-1的阶乘是什么?是n-1乘以n-2的阶乘。那n-2的阶乘又是什么?这样下去永远也没完。因此需要定义一个最关键的基础条件(Base Case):0的阶乘等于1

      0! = 1
      n! = n*(n-1)!
      
  • 下面我们来用code完成这个简单的计算过程

    • 先把Base Case写进去

      int factorial(int n)
      {
          if(n==0)
              return 1;
      }
      
    • 如果参数n不是0,该return什么呢?

      • 应该return n*factorial(n-1)

      • 为了下面的分析方便,我们引入几个临时变量把这个语句拆分一下:

        int factorial(int n)
        {
            if(n==0)
                return 1;
        	else {
                int recurse = factorial(n-1);
                int result = n * recurse;
                return result;
            }
        }	
        

        factorial这个函数居然可以自己调用自己?是的。自己直接或间接调用自己的函数称为递归函数

  • 这里的factorial是直接调用自己,有些时候函数A调用函数B,函数B又调用函数A,也就是函数A间接调用自己,这也是递归函数

  • 可以把factorial(n-1)这一步看成是在调用另一个函数:另一个有着相同函数名和相同代码的函数调用它就是跳到它的代码里执行,然后再返回factorial(n-1)这个调用的下一步继续执行。我们以factorial(3)为例分析整个调用过程,如下图所示:

    factorial(3)的调用过程

    图中用实线箭头表示调用,用虚线箭头表示返回,右侧的框表示在调用和返回过程中各层函数调用的存储空间变化情况

    • main()有一个局部变量result,用一个框表示
    • 调用factorial(3)时要分配参数和局部变量的存储空间,于是在main()的下面又多了一个框表示factorial(3)的参数和局部变量,其中n已初始化为3
    • factorial(3)又调用factorial(2),又要分配factorial(2)的参数和局部变量,于是在main()factorial(3)下面又多了一个框。每次调用函数时分配参数和局部变量的存储空间,退出函数时释放它们的存储空间。factorial(3)factorial(2)是两次不同的调用,factorial(3)的参数nfactorial(2)的参数n各有各的存储单元,虽然我们写代码时只写了一次参数n,但运行时却是两个不同的参数n。并且由于调用factorial(2)factorial(3)还没退出,所以两个函数调用的参数n同时存在,所以在原来的基础上多画一个框。
    • 依此类推,请读者对照着图自己分析整个调用过程。读者会发现这个过程和前面我们用数学公式计算3!的过程是一样的,都是先一步步展开然后再一步步收回去。

    我们看上图右侧存储空间的变化过程,随着函数调用的层层深入,存储空间的一端逐渐增长,然后随着函数调用的层层返回,存储空间的这一端又逐渐缩短,并且每次访问参数和局部变量时只能访问这一端的存储单元,而不能访问内部的存储单元,比如当factorial(2)的存储空间位于末端时,只能访问它的参数和局部变量,而不能访问factorial(3)和main()的参数和局部变量。具有这种性质的数据结构称为堆栈或栈(Stack)随着函数调用和返回而不断变化的这一端称为栈顶每个函数调用的参数和局部变量的存储空间(上图的每个小方框)称为一个栈帧(Stack Frame)

    操作系统为程序的运行预留了一块栈空间,函数调用时就在这个栈空间里分配栈帧,函数返回时就释放栈帧

  • 写递归函数时一定要记得写Base Case,否则即使递推关系正确,整个函数也不正确。如果factorial函数漏掉了Base Case:

    int factorial(int n)
    {
    	int recurse = factorial(n-1);
    	int result = n * recurse;
    	return result;
    }
    

    那么这个函数就会永远调用下去,直到操作系统为程序预留的栈空间耗尽程序崩溃(段错误)为止,这称为无穷递归(Infinite recursion)

  • 递归不只是为解决数学题[8]而想出来的招,它是计算机的精髓所在,也是编程语言的精髓所在

    • 其实expression和statement也是递归定义的

      • expression

        表达式 → 表达式(参数列表) 
        参数列表 → 表达式, 表达式, ...
        
      • statement

        语句 → if (控制表达式) 语句
        
  • 递归和循环其实是等价的


  • 这一节写的也精彩,又渗透了很多编程思想,还推荐了很多经典书籍,如SICP,编译原理龙书等
    • 值得反复回看:http://akaedu.github.io/book/ch05s03.html

我猜有递归可视化工具,一搜果真有收获

  • Data Structure Visualizations这个网站里面有

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZUt0Oknb-1674408367332)(null)]

  • https://recursion.vercel.app/这个体验也很棒

    image-20230123004521185

    去广告版:https://quanticdev.com/tools/recursion-visualization/

    • GitHub链接:https://github.com/brpapa/recursion-tree-visualizer
  • visualgo网站:

    https://visualgo.net/en/recursion

  • python turtle可以实现可视化

    • https://runestone.academy/ns/books/published/pythonds3/Recursion/VisualizingRecursion.html

习题

GCD(Greatest Common Divisor) 最大公约数

1、编写递归函数求两个正整数a和b的最大公约数(GCD,Greatest Common Divisor),使用Euclid(欧几里得)算法: 如果a除以b能整除,则最大公约数是b。 否则,最大公约数等于b和a%b的最大公约数。

int gcd(int a, int b)
{
    if( a % b == 0)
        return b;
    else
        return gcd(b, a%b)
}

Fibonacci

2、编写递归函数求Fibonacci数列的第n项,这个数列是这样定义的:

fib(0)=1
fib(1)=1
fib(n)=fib(n-1)+fib(n-2)

int fibonacci(int n)
{
    if( n == 0 || n == 1)
        return 1;
   	else
        return fibonacci(0) + fibonacci(1);
}

相关资源、参考资料

  • 豆瓣评价

  • 开源电子书

  • 《Linux C编程一站式学习》这书写得很不错,为什么都买不到了呢? - echo1937的回答 - 知乎 https://www.zhihu.com/question/34069391/answer/544825938

  • [大佬们的学习笔记]

    • 习题答案整理
  • https://blog.csdn.net/weixin_44576779/article/details/87443584

  • echo1937的回答 - 知乎 https://www.zhihu.com/question/34069391/answer/544825938

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在linux中安排mysql

linux安装mysql 检测当前系统中是否安装Mysql数据库 rpm -qa rpm -qa|grep mysql rpm -qa|grep mariadb没有输出就是没有安装 我的这里显示mariadb是安装了的&#xff08;会与mysql冲突&#xff09; 卸载已经安装的软件 rpm -e --nodeps 软件名称 rpm -e --nodeps mariadb-li…

什么是执行董事

一、什么是执行董事执行董事&#xff0c;是指参与经营的董事。作为法定意义上的执行董事&#xff0c;是指规模较小的有限公司在不设立董事会的情况下设立的负责公司经营管理的职务。作为上市公司意义上的执行董事&#xff0c;执行董事并没有明确的法规依据。执行董事和非执行董…

偷偷理解Java和Scala中==和equals()的区别

君霸王&#xff0c;社稷定&#xff0c;君不霸王&#xff0c;社稷不定&#x1f97d; 目录 Java总结 Scala总结 Java中和equals() ---------------------------------------------------------------------------------------------------------------------------------------…

【人工智能原理自学】卷积神经网络:图像识别实战

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本文讲解卷积神经网络&#xff1a;图像识别实战&#xff0c;一起卷起来叭&#xff01; 目录一、“卷”二、LeNet-5网络一、“卷” 这节课我们来看如何把卷积运算融入到神经网络…

【青训营】Go语言的基本语法

一、 配置Go语言及其开发环境 Mac配置&#xff1a;http://t.zoukankan.com/zsy-p-6685889.html https://wenku.baidu.com/view/8aeec92b15fc700abb68a98271fe910ef12daeaf.html?wkts1673764660043&bdQuery%E5%A6%82%E4%BD%95%E9%85%8D%E7%BD%AEgopathmac 二、基础语法 p…

避免用Apache Beanutils进行属性的copy。why?让我们一起一探究竟。

在实际的项目开发中&#xff0c;对象间赋值普遍存在&#xff0c;随着双十一、秒杀等电商过程愈加复杂&#xff0c;数据量也在不断攀升&#xff0c;效率问题&#xff0c;浮出水面。 问&#xff1a;如果是你来写对象间赋值的代码&#xff0c;你会怎么做&#xff1f; 答&#xf…

05 |「链表」刷题

前言 前言&#xff1a;链表面试高频题。 文章目录前言一. 基础回顾二. 高频考题1. 例题1&#xff09;题目链接&#xff08;LeetCode 206 反转链表&#xff09;2&#xff09; 算法思路3&#xff09;源码剖析4&#xff09;时间复杂度2. 习题一. 基础回顾 参考上一讲&#xff1a; …

线性代数[向量]

系列文章目录 第一章 线性代数[初等变换(一)] 第二章 线性代数[初等变换(二)] 第三章 线性代数[初等变换(三)] 第四章 线性代数[矩阵的秩] 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一.引入 二.向…

计网必会:运输层概述、网络层介绍、TCP、UDP、多路复用、多路分解

文章目录运输层概念回顾TCP 和UDP概述介绍网络层TCP UDP网络层的联系多路复用和多路分解多路复用和多路分解的中国话理解TCP的多路复用和多路分解与UDP的区别HTTP 会话Web服务器和TCPUDP的多路复用和多路分解源端口号的用处的中国话理解UDP套接字无连接运输UDP 的优势运输层概念…

【C语言进阶】指针进阶(详细版)

目录 一、字符指针 二、指针数组 三、数组指针 1、数组指针的定义 2、&数组名和数组名的区别 3、数组指针的使用 四、数组传参和指针传参 1、一维数组传参 2、一级指针传参 3、二维数组传参 4、二级指针传参 五、函数指针 1、函数指针的定义 2、函数指针的使用 六、…

如何与他人交流

上期我们讲了打破预期,顺应主体,我的别人交流,只有在不把别人当成对象(工具人),而是把对方当成主体的情况下(让别人感受到尊重),这是相互尊重的终极本质,也是唯一方法.把别人当人看.认同对方,对方也会认同你.自信从何而来自信本意为相信自己,所以自信本应该是由内而外的事物,但…

【MySQL进阶】MySQL事务详解

序号系列文章5【MySQL基础】字符集与校对集详解6【MySQL基础】MySQL单表操作详解7【MySQL基础】运算符及相关函数详解8【MySQL基础】MySQL多表操作详解文章目录前言1&#xff0c;事务概念2&#xff0c;事务四大特性2.1&#xff0c;原子性2.2&#xff0c;一致性2.3&#xff0c;隔…

ORB SLAM3 ubuntu18.04 ROS 运行 段错误 (核心已转储) 踩坑及解决

问题猜测及解决&#xff1a;opencv版本兼容性 项目版本&#xff1a;ORB SLAM3 V1.0版本 CPU&#xff1a;13600K (大小核架构不知是否会影响) 电脑环境&#xff1a;ubuntu18.04 ROS运行 相机&#xff1a;D435 i opencv版本&#xff1a;3.2 and 4.6 前提&#xff1a;编译无报错&a…

Java 23种设计模式(3.创建者模式-原型模式)

1.概述 用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象。 2.结构 原型模式包含如下角色&#xff1a; 抽象原型类&#xff1a; 规定了具体原型对象必须实现的的 clone() 方法。 具体原型类&#xff1a; 实现抽象原型类的 clon…

多线程(5)

文章目录前言 &#xff1a;常见锁策略了1.悲观锁 VS 乐观锁2. 轻量级锁 VS 重量级锁3.自旋锁 VS 挂起等待锁4. 读写锁 VS 普通的互斥锁5. 公平锁 和 非公平锁6. 可重入锁 VS 不可重入锁CAS1. CAS 的应用场景2. CAS 的典型问题 : ABA 问题synchronized 原理1.锁升级 / 锁膨胀2.锁…

Ae 效果详解:发光

效果/风格化/发光Effects/Stylize/Glow发光 Glow效果可找到图像中的较亮部分&#xff0c;然后使那些像素和周围的像素变亮&#xff0c;以创建漫射的发光光环。可以创建两种颜色&#xff08;颜色 A 和颜色 B &#xff09;之间的渐变发光&#xff0c;并可通过复制发光效果以创建更…

RESTful开发风格 与 SpringMVC跨域访问

RESTful REST&#xff1a;表现层状态转换&#xff0c;资源在网络中以某种表现形式进行状态转移RESTful 是基于 REST理念 的一套开发风格&#xff0c;是具体的开发规则&#xff0c;如果一个架构符合REST 原则&#xff0c;就称为 RESTful 架构。 RESTful 开发规范&#xff1a;…

【leetcode】学了栈和队列却觉得无用武之地?试试这几道题目吧!

目录 0.写在前面 1.leetcode.20 有效的括号 2.leetcode.225 用队列实现栈 3.用栈实现队列 4.设计循环队列 0.写在前面 这些题目所用语言为C语言&#xff0c;由于C语言未提供栈和队列的数据结构&#xff0c;所以需要我们手动实现栈和队列。此外熟练掌握栈和队列的性质对解…

【C++】从0到1入门C++编程学习笔记 - 实战篇:职工管理系统

文章目录一、需求分析二、创建项目2.1 创建新项目2.2 添加文件三、创建管理类3.1 创建文件3.2 头文件实现3.3 源文件实现四、菜单功能4.1 添加成员函数4.2 菜单功能实现4.3 测试菜单功能五、退出功能5.1 提供功能接口5.2 实现退出功能5.3 测试功能六、创建职工类6.1 创建职工抽…

虚拟机克隆两网卡冲突

常见网卡设置 vim /etc/sysconfig/network-scripts/ifcfg-ens33TYPEEthernet BOOTPROTOstatic DEFROUTEyes NAMEens33 UUID025f7880-7357-4148-ae5a-a629d597c133 DEVICEens33 ONBOOTyes DNS18.8.8.8 IPADDR 192.168.100.16 GATEWAY192.168.100.254 NETMASK255.255.255.0有的版…