C语言学习笔记—关于函数的一些细节

0、简述

        C是一种过程式编程语言,在C语言中,函数是能实现一定功能的程序段,是C程序的构建块。因此,了解它们是什么,它们的行为方式,以及在进入或离开函数时发生了什么非常重要。

        函数跟普通变量是类似的,不同的是,函数存储算法,而普通变量存储数值。通过将变量和函数组合成一个新的类型,我们可以在同一个概念下存储相关的值和算法,这就是面向对象编程(OOP)所做的。

一、函数的剖析

        函数可以想象成是一个逻辑盒子,它包含名称、输入参数列表、输出结果列表。在C语言和许多其他受C影响的编程语音中,函数只返回一个值。在面向对象的语言(C++和Jave)中,函数(通常被称为方法)也可以抛出异常。C的情况则不然,函数由函数调用引发,函数调用只是使用函数的名称来执行其逻辑。正确的函数调用应该传递所有必需的参数给函数并等待其执行。

                注意:在C语言中函数总是 阻塞式 的,这意味着 主调函数 必须等待 被调函数 完成,然后才能收集返回的结果。

        与阻塞函数相反,还有非阻塞函数。当调用非阻塞函数时,调用者不用等待被调函数完成就可以继续执行。在这种方案中,通常有一个回调机制,在被调用函数完成时触发

        非阻塞函数也称为异步函数,在C语言中没有异步函数,所以需要使用多线程解决方案来实现它们。现在人们越来越喜欢使用非阻塞函数,它们通常被称为面向事件编程。这种编程方法以非阻塞函数为中心,而且大多数编写的函数都是非阻塞的。 在面向事件的编程中,实际的函数调用发生在事件循环内部,并且在事件发生时触发适当的回调。

二、设计的重要性

        函数是过程式编程的基本构建块。由于函数得到编程语言的正式支持,对编写代码的方式产生了巨大的影响。编写函数相当于只编写一次某个特定的逻辑,将逻辑存储在半可变实体中,并在需要时随时随地调用它们

        此外,函数允许我们对其他现有逻辑隐藏一段逻辑。换句话来说,它们在不同的逻辑组件之间引入了一个抽象层。举个例子,假如有一个函数avg,它计算输入数组的平均值,还有另一个函数main,main函数调用avg函数。就可以理解说,avg函数的逻辑对main函数中的逻辑而言是隐藏的。因此,如果想改变avg内部的逻辑,并不需要改变main函数中的逻辑。这是因为main函数仅依赖于avg函数的名称和可用性。

        可见,一份优秀优雅的工程代码,其中的模块化程序设计是多么重要的环节。

三、栈管理

        栈段是用于为所有的局部变量、函数参数列表、函数返回值等分配内存时的默认存储位置。因此,在函数中声明一个局部变量时,实际是在栈段中为其分配内存,这种分配总是发生在栈段的顶部。栈段顶部的量也是最早被移除的量。

        栈段也用于函数调用,当调用一个函数时,一个包含返回地址和所有传递参数的栈帧被放在栈段的顶部,然后才执行函数逻辑。当从函数返回时,栈帧弹出,接着执行由返回地址寻址的指令,通常是继续执行主调函数。

        函数体中声明的所有局部变量都放在栈段的顶部。因此,当离开函数时,所有相应的栈变量都被释放,这也可以解释为什么一个函数不能访问另一个函数中的变量。这种机制也解释了为什么在进入函数之前和离开函数之后,局部变量是未定义的。

        综上,理解栈段及其工作方式对于编写正确的和有意义的代码至关重要。它可以防止发生常见的内存错误,也提醒着不能在栈上创建任意大小的变量。栈是内存中一段有限的空间,你可能会填满它,并发生潜在的栈溢出错误。这种错误通常在太多的函数调用时发生,因为这时栈帧消耗了所有的栈内存空间。在处理递归函数时,如果没有任何中断条件或限制时,栈溢出的情况就会经常发生

四、值传递和引用传递

        在C语言中只有值传递!

        C中没有引用,所以也没有引用传递。所有的内容都被赋值到函数的局部变量中,并且在离开函数后不能读取或修改它们。

        接下来看如下例子

        例1:

        

        这里,我们很容易就预测到了输出,变量x的值没有发生变化,因为它是通过值传递的。

        例2:

        

        :指针传递函数调用的例子,它不同于引用传递。

        如输出所示,在函数调用之后,指针本身所指的地址并没有改变,这意味着指针参数是按值传递的。在func函数内部对指针进行解引用可以访问指针所指向的变量,可以看做是函数内部复制的形参指针和函数外部的实参指针都指向了同一个地址,故对形参指针解引用可以访问并修改外部变量。但形参指针本身是值传递,相当于是复制了实参的指针,形参指针和实参指针都指向了同一块内存空间。

        可以发现,在func函数内部修改了形参指针的指向并不会改变主函数中实参指针的指向。故在C语言中,在函数调用期间,所有的形参都是通过值传递的,对指针解引用允许修改主调函数中的变量。

        在这个例子中,我们将变量的指针传递给函数,而不是直接传递变量本身。通常建议使用指针作为形参,而不是将对象直接传递给函数。为什么呢?因为复制8个字节的指针实参比复制上百个字节的大数据量的对象要高效的多。

        拓展:如果想在局部函数内部修改外部变量的值 或 局部变量在退出函数后不被释放,前者可以使用指针传递,后者可以使用static静态声明。

    

           

  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值