第九章 - DDL-Killer/The-road-of-Linxu-Group2024 GitHub Wiki

printf函数本质:int printf(const char *format,.....)

变量初始化

  • va_listva_start
  • va_list:访问可变参数列表中的数据,这是一个特殊的数据类型,用于存储变长参数的列表信息
  • va_start:用于初始化va_list变量,使它指向变长参数列表中的第一个参数,使我们可以依次读取每个参数

循环解析格式字符串

  • 使用whlie遍历字符串
  • 遇到%,跳过,然后判断后面的类型
  • 如果没有,则直接存入缓冲区

解析格式说明符

  • 使用va_arg()从va_list类型中取出一个目标类型的参数,给snprintf转换
  • 使用snprintf:把类型进行转换,把字符串的“%d”等等转换为转换说明存入缓冲区

snprintf:int snprintf(char *str,size_t size,const char *format,...)

  • str:目标缓冲区,格式化之后的数据写入这个字符串
  • size:指定缓冲区的大小
  • format:格式化字符串,定义输出的格式
  • ...:可变参数,根据format说明符传入要格式的值

输出结果

  • write(1,buffer,count)将缓冲区的内容写入标准输出(1代表标准输出)——控制台

清理和返回

  • 使用va_end()结束可变参数的读取
  • return count返回成功写入的字符数

代码

Screenshot from 2024-10-26 15-10-50

复习函数

  • 函数是完成特定任务的独立程序代码单元
  • 语法规则定义了函数的结构和使用方式

创建并使用简单函数

  • 函数原型指明了函数的返回值和函数接受的参数类型,这些信息称为函数的签名
  • 函数中的变量是局部变量,该变量只属于starbar()函数

定义带形式参数的函数

  • 函数名右边括号里面的变量称为形式参数

调用带实际参数的函数

  • 实际参数提供了形式参数的值
  • 实际参数要被求值,然后将值拷贝给被调函数的形式参数

黑盒视角

  • 在调用函数中的私有局部变量不影响外面的主调函数变量

使用return从函数中返回值

  • 设计用于测试函数的程序有时被称为驱动程序
  • return另一个作用是,终止函数并把控制传给主调函数的下一条语句

函数类型

声明函数时必须声明函数的类型,带返回值的函数类型应该与其返回值类型相同

函数原型的优点

  • 函数原型是一个强有力的工具,它让编译器捕获在使用函数时可能出现的许多错误或疏漏

递归

C允许函数调用它自己,这种调用过程称为递归

尾递归

  • 最简单的递归形式是把递归调用置于函数的末尾,正好在return语句之前
  • 尾递归是最简单的递归形式

递归和倒序计算

  • 设计一个以二进制表示整数的方法和算法:
  1. 在二进制中,奇数的末尾一定是1,偶数的末尾一定是0,通过n%2,确定最后一位是是1还是0,这个数是最后打印的
  2. 要获得下一个数字,就n/2,这种方法相当于把小数点往前移动一位,然后继续n%2
  3. 第二步的结果小于2则停止计算,只要结果大于或等于2,则说明还有二进制位

递归的优缺点

  • 优点:为某些编程问题提供了最简单的解决方案
  • 缺点:会快速消耗计算机的内存资源,不方便阅读

编译多源代码文件的程序

**使用多个函数最简单的办法就是把它们都放在同一个文件里,然后像编译只有一个函数的文件那样编译

C语言的编译过程:预处理、编译、汇编、链接

  1. 预处理
    • 宏替换
    • 文件包括
    • 条件编译
    • 删除注释
  2. 编译
    • 语法分析
    • 语义分析:变量,函数,数据类型
    • 优化:代码优化、优化循环
  3. 汇编
    • 将编译生成汇编语言代码,转为机器代码
  4. 链接:多个目标文件与库文件结合在一起,生成可执行文件
    • 符号解析:函数和变量引用链接在一起
    • 库的加载:外邻库加载到最终可执行
    • 地址条件:分配内存地址

UNIX和Linux

  • GNU C编译器和编译器GCC,编译后生成a.out可执行文件

DOS命令

  • 拓展名为.obj

Windows和苹果的IDE编译器

  • 集成开发环境是面向项目的
  • 项目描述的是特定程序使用的资源,包括源代码文件
  • IDE不用在项目列表中列出头文件,只需要管理使用的源代码文件

使用头文件

  • 如果把main函数放在第一个文件里,把函数定义放在第二个文件里,那么第一个文件仍然要使用函数原型
  • 把函数原型放在头文件中,就不用再每次使用函数文件时都写出函数的原型
  • 程序中经常用C预处理定义符号常量,更好的办法是,把#define指令放进头文件,然后在每个源文件中使用#include指令包含该文件即可
  • 第一个程序清单包含main()函数,提供整个程序的组织结构,第二个函数清单包含支持的函数,假设这些函数在独立的文件中,最后还列出了头文件,包含了改程序中所有的自定义符号和函数原型
  • #include"hotel.h"指令中的双引号表明被包含的文件位于当前目录中
  • memu()函数:测试scanf()的返回值来跳过非数值数据

查找地址:&运算符

  • 指针是C语言最重要概念之一,用于储存变量的地址
  • 一元&运算符给出变量的存储地址
  • %p是输出地址的转换说明

指针简历

  • 指针是一个值为内存地址的变量(数据对象)

间接运算符:*

假设已知:ptr = &bah,使用间接运算符 * 找出储存在bah中的值,也被称为解引用运算符

声明指针

  • 类型说明符表明了指针所指向对象的类型,星号(*)表明声明的变量是一个指针