书籍信息

《Linux C程序设计大全》(吴岳版)封面图

书名 《Linux C程序设计大全》
作者 吴岳
版次 2009年2月第1版
ISBN 9787302192114
  • 笔记目录与书籍略有不同
  • 部分编号有误
  • 未包含整本书内容

二. 控制结构

  1. 错误判断(goto

    1. 错误时,goto至错误处理语句(析构),释放未完成资源

    2. 释放顺序与构建顺序相反

      e.g.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      文件不存在,goto err1
      文件打开失败,goto err2
      所需资源分配失败,goto err3
      內容读取失败,goto err4

      err4: free 3分配的资源
      err3: 关闭2打开的文件
      err2:
      err1:
      返回错误码
  2. 短路计算

    1. 可使用位运算代替乘法和除法中的2倍数,尽量避免使用除法
  3. 循环语句

    1. 条件中避免使用函数,避免造成函数的重复调用 (见三-1-1.
    2. 循环条件中的变量可使用局部变量(尽量避免使用全局变量作为循环条件) (见二-5-1.
  4. 条件判断

    1. switch有一个跳转表,执行速度很快,但跳转表会占用更多空间
  5. 控制结构的优化

    1. 各控制结构之间可互相替换(基本符号等除外)
    2. switch以空间换实间
    3. if…else…连接判断应以概率大的选项为首,依次排序
    4. 尽量使用+=-=++--代替=,可节省时间和内存。(相对复杂的语句的除外)
    5. 避免将循环算子使用率高的变量设置为全局/静态变量,编译器会自动优化至寄存器,全局/静态变量是在内存中 (同三-5-1)

三. 函数

  1. 函数的本质

    1. 函数名是一种指针,局部变量存储于栈中,用后即毁 (见四-1.
    2. 访问函数时会经过:函数入栈→保存寄存器值→保存返回地址→跳转 (见三-6-1.
  2. 声明与定义

    1. 声明:仅告知编译器变量的存在,不分配存储空间。

      e.g.
      1
      int a;
    2. 定义:告知编译器变量的存在,且分配存储空间。赋值不是定义

      e.g.
      1
      int a=3; //此为定义
    3. 当变量的作用域内只有声明而没有定义时,编译器会自动将第一个声明默认为变量的定义。

      e.g.
      1
      2
      int a; //此为声明,因没有定义,默认为定义
      a=3; //这里仅仅是赋值
    4. 多文件的符号的声明与定义规则:

      1. 不允许对同一个符号(变量或函数)有多个定义
      2. 如果该符号有一个定义和多个声明,则编译器选择被定义的符号
      3. 如果该符号有多个声明,则从其中任选一个作为符号的定义
  3. 全局变量

    1. 全局变量(函数外定义),存储与数据段,作用于从该行开始至整个程序的结束 (见三-)

      e.g.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      int add(int a, int c)
      {
      //b暂未声明,引发错误
      return a + b + c;
      }
      int b=10; //b的作用域为从声明开始至程序结束。
      int mule(int a)
      {
      //此处的b为全局变量b
      return a * b
      }
    2. 初始值为0,自动初始化

    3. static声明的全局变量,作用域将改变为本文件 (见三-5-1.

  4. 局部变量

    1. 作用于函数/复合语句内,存储于堆栈内,用后即毁
    2. 不会自动初始化,数值随机
    3. static声明的局部变量,作用域不变,不再销毁 (见三-5-1.
  5. static的使用

    1. static声明的全局变量生命周期不变,作用域缩小至本文件
    2. static声明的局部变量作用于不变,生命周期为整个程序执行期间
    3. static声明的函数作用域为本文件,以实现封装和模块化 (见三-7-1.
  6. 优化

    1. 因函数是以时间交换空间,应尽量减少函数的调用
    2. 调用频率很高的局部变量会存储与寄存器内,但全局变量依旧存储于数据段,故应将高频率使用(如循环算子)设置为局部变量 (同二-5-1.
    3. register类型:寄存器变量,被声明的变量优先分配至寄存器
  7. 多文件程序

    1. auto类型:自动类型,由编译器自动决定存储位置和性质
    2. extern类型:外部变量,函数默认为extern类型,使其可以被外部文件调用
    3. 使用static改变函数和变量的生命周期 (见三-1.
    4. C语言中一个文件就是一个模块。
    5. static声明的变量和函数无法被外部文件访问,增加文件的安全性,以此实现封装和模块化,使文件隐藏细节,只对外暴露接口。⚠修改模块时,尽量不要修改接口
    6. 链接规则
      1. 符号的声明与定义 (见三-2-1.
  8. 可变参数

    1. 引用:stdarg.h

      宏定义 说明
      va_list parameter 接收到的包含所有参数的参数列表
      void va_start(va_list,first parameter) 定位至第一个参数
      type va_arg(va_list, type) 得到下一个type类型的参数
      void va_end(va_list) 参数处理结束

      详见C 标准库 - <stdarg.h>|菜鸟教程

四. 指针

  1. sizeof,屏蔽细节,使其具有更好的移植性
  2. 指针
    1. 指针别名 (见四-)
    2. type (*p) [size]
      • p为数组的指针,应用整个数组;p++表示指针跳过整个数组,获取数组内容需转换为type*类型
    3. 函数的参数为原值的副本,修改变量需传递变量的指针,修改指针需传递指针的指针…
    4. void *
      • 指针本质为无符号整型,但C语言不允许不同类型的指针比较。可用void*代替任意类型的指针(未说明该指针指向的数据占用空间的大小),void*类型可以参与运算,但无法调用其指向的数据
  3. 函数指针
    1. type (*p) (parameterlist)
      • 常被用于回调函数,type常被定义为void类型,`void`被当作泛型,损失效率
    2. 函数指针数组 type (*p[size])(parameterlist)
  4. const
    1. const修饰的变量不可改变,必须在定义的时候初始化
    2. const *
      • const type *p=&a,可更改p的指向,不可通过指针修改内容
      • int * const p=&a,不可更改p的指向,可通过指针修改内容
      • const type * const p=&a,不可修改p的指向,不可通过指针修改内容。
    3. 修饰函数的传参和返回值,优化编译 (见)
    4. 优点
      1. 指明该变量不可更改
      2. 便于调试。编译器会发现const修改无效,否则只会显示内存访问错误
      3. 宏替换是数据,会造成多次数据拷贝;而const只是一个地址,只有一份数据
      4. const定义的常量通常不分配存储空间,而是存放在符号表中,即没有读取内存的操作,提高程序效率
    5. 其他
      1. C中的const修饰全局变量作用域已就位global,而C++const修饰的全局变量作用域为local,需使用extern将作用域变为global。其他区别略
      2. define在预处理阶段展开,没有类型检查,展开时并不会分配内存;const在编译运行阶段使用,编译阶段进行类型检查,一般会在内存中分配一份数据
      3. const变量传递给const变量会将变量限制为制度类型,相反却会警告或者报错

五. C语言高级命令

  1. 结构体

    1. 为了提高速度,结构体使用了内存对齐,以空间换取时间,合理改变结构体的顺序可以优化存储空间,但嵌入式等系统中为了节省存储空间一般会禁用对其机制。
  2. 位运算

    1. 掩码运算:获取所需字节/标志位信息。为了节约存储空间,通常将多个标识符存储于一个字节中。
  3. 预处理

    1. 本模块函数使用static(功能类似于private(见三-3-1.

    2. 头文件内容

      区域 内容
      头文件区 其他头文件
      全局宏区 共定义(如调试开关、缓冲区大小等)
      全局变量区 非static函数/变量的声明
      函数接口区 所有模块的函数接口
      常用文件名 说明
      comon.h 结构体,声明函数接口,全局变量
      list.c 函数内容,所有函数均为接口
      main.c main函数,包含common.h头文件即可
    3. 调试开关

      • 使用条件编译,使代码调试段不再编译,不必依次注释调试语句

        e.g.
        1
        2
        3
        4
        5
        6
        7
        8
        #define DEBUG 1 //调试开关
        #ifdef DEBUG //调试语句
        #define PRINT(str) printf(str);
        #define PRINT!(str, arg); printf(str, arg);
        #else //若调试开关关闭,则调试语句无效
        #define PRINT(str);
        #define PRINT!(str, arg);
        #endif
    4. inline:将函数在编译时直接展开到代码段,执行函数不再跳转,提高执行效率。类似于宏(不是宏)。 只有短的,少量的函数会被展开,是否展开由编译器决定。对递归无效

    5. restrict:确定指针参数的唯一性。用于优化编译。