🤖 AI文章摘要 gemini-2.0-flash-lite

这篇文章介绍了C语言中的函数、变量作用域以及声明和定义。

关于函数,文章阐述了其定义格式、参数(形参和实参)以及两种主要的传参方式:值传递和地址传递,并提到了函数指针作为参数的优势。此外,还解释了递归调用的概念,强调了中断函数的重要性,并简要提到了静态代码体和main函数。文章还深入探讨了函数的实现原理,即利用栈结构,并列举了cdecl、stdcall和fastcall三种调用约定。

在变量作用域方面,文章区分了局部变量、静态局部变量和全局变量,并说明了它们各自的生命周期和存储区域。

最后,文章详细解释了声明和定义的区别,指出声明表明对象的存在性,而定义则包含声明和对象的具体细节。文章还阐述了staticextern关键字的作用,以及它们如何影响变量和函数的连接性。此外,还提及了函数的类型以及函数指针的定义和类型别名。

函数

  • 定义函数: Return_type func(arg1,arg2,...) {...}
  • 函数的参数: 函数参数作为函数执行的初始参数, 可以为void, 当形参和实参类型不同时会自动隐式类型转换.
    • 形参: 函数运行前的初始参数, 它和函数调用者没有任何关系
    • 实参: 函数调用者传入的参数, 它是属于调用者的
  • 传参方式: 调用者调用函数时传入实参, 函数执行前会用实参初始化形参(此过程类似于赋值),
    • 值传递: 使用值作为参数传递, 操作的是原始数据的副本, 对原始数据不影响. 开销较大, 安全.
    • 地址传递: 使用指针作为参数传递, 操作的是同一块内存空间. 开销较小, 较不安全.
    • 函数指针作为参数: 使用函数指针作为参数的好处是可以模块化编程, 对于核心功能的改变, 不用修改源代码, 只需修改传入的函数指针即可.
  • 递归调用: 递归就是自己调用自己, 写递归时要考虑中断函数怎么写, 不然一定会栈溢出.
    • 中断函数: 指的是再继续递归调用前用于终止调用的代码
    • 递归示意图:
  • 静态代码体: 静态代码体定义在函数中的一对{ }内, 它是一个不带参数的匿名函数, 只能执行.
  • main函数: 是整个程序执行的入口, 它的参数固定为(int argc, int * argv[])表示参数的个数与存放参数的指针数组
  • 函数实现原理: 函数是使用栈结构来实现的. 每一个进程都有一个函数栈.
    • 主调函数将函数的形式参数按照调用惯例压栈创建
      • cdecl 压栈顺序是从右到左,主调函数出栈清理 下划线+函数名
      • stdcall 压栈顺序是从右到左,被调函数出栈清理 下划线+函数名+@+参数
      • fastcall 压栈顺序是从右到左,被调函数出栈清理 @+函数名+@+参数的字节数
    • 将返回时执行的下一条指令压栈
    • 进入被调函数,新声明的函数变量压栈创建
    • 函数返回前将执行期间的变量弹栈. 函数参数按照调用惯例确定清理方, 返回值通过exa寄存器返回
  • 可变参数: 待填坑http://c.biancheng.net/view/344.html

变量的作用域

  • 局部变量:定义在函数内部的变量, 作用在到函数结束位置, 根据函数的实现原理它会在函数执行结束时释放.
  • 静态局部变量: 使用static修饰, 它被保存在静态区, 程序退出时才会释放
  • 全局变量:定义在函数外部的变量, 作用在整个项目中. 它只有在程序退出时才会被释放.

声明和定义

声明即表明对象的存在性, 定义是精确定义对象的内部细节, 在声明时进行. 通常将未定义的声明称为声明, 它不占用任何内存空间, 仅仅指明对象的存在性 已定义的声明称为定义, 它占用内存空间, 指明了对象的存在性, 也进行了定义 注意前边定义的变量不管有没有初始化都叫定义!!!! 2. static与extern: ->extern声明扩展到外部使用的函数或全局变量(其他文件能使用, 使用前需要先声明) ->C语言的函数和全局变量默认外部扩展省略了extern ->static声明内部连接使用的函数, 全局变量或局部变量(其他文件不能使用) ->静态变量存在于静态区,程序执行期间都有效,不会被释放 ->变量同名时局部变量优先于全局变量


函数类型: 函数的内部是一系列指令, 被存放在程序的代码区. - RetType (Ty1 arg1, Ty2 arg2,…){/*def*/},

  • 函数指针: Type(*)(Type1 ,Type2 ,… ),
    • 指针类型别名: typedef Type(*alias)(Type1 ,Type2 ,… )