Skip to content

首先以一个轻松的视频课程了解 c, 然后

PS:以下关于标准库的内容没有详细写, 我不认为这些内容具有可概括性

c prime plus

正如它的书名所示, 它很好地介绍了 C 语言的主要内容, 并且颇有广度与深度, 读这本书确实能让人受益匪浅 但是要注意还缺少几部分内容:

  • C 语言经验, 这会在接下来的几本书当中补全
  • C 语言极深的细节, 这不一定是有效的, 但是如果你想要了解, 也许只有阅读 C 标准
  • C 语言的的编译链接以及硬件层面的细节, 在汇编语言, 计算机系统基础, 计算机组成原理与体系结构, 编译原理这些内容中提及, 但也许相当具体的内容需要了解编译器实现

初识 c 语言

  • 选择 c 语言作为程序设计基础语言的原因/c 语言的底层性, 例如最接近汇编, 指针有利于理解内存模型
  • c 语言的设计宗旨/自由与快 为了快可以牺牲移植性
  • 程序设计步骤/不要忽视非编写的步骤, 当你的程序变得复杂, 它们显得格外重要

c 语言概述

  • 如题

ps:c 语言 6 大基本语句, c 将赋值也认为一种运算符表达式, 声明不是表达式

数据与 c

  • 浮点型精度 6/13
  • 对字面量的修饰很重要 (警惕自动类型转换)
  • 字符/'FATE'赋值等同于'E', 因为''在 c 中往往是 int 型
  • 转义字符可以直接加编码
  • 字面量的默认类型 int/double
  • 刷新输出缓冲区 (满/遇到\n/需要输入)fflush()强制刷

字符串和格式化输入/输出

  • sizeof 类型必须加() 返回类型是 lu/llu (比较时注意类型)
  • printf 修饰符 # (完全展示)
  • c 会将连续字符串融合
  • 注意 scanf()的读取过程/缓冲区中留下了什么
  • *在 pri 中是限制宽度(读取一个参数), scan 中是跳过
  • 函数参数在栈中, 根据类型读取长度, 但是物理先后由实现决定

运算符 表达式与语句

  • C 标准中的数据对象/左值用于标识存储位置/对象定位值
  • 除法趋零截断
  • 只有共享运算对象时才考虑结合律/否则由编译器决定
  • a%b 等于 a-(a/b)*b
  • 不要在变量多次出现时使用++--, 因为顺序不确定
  • c 只保证序列点之前完成副作用

PS:关于复合语句 (块), 它的值是最后一个语句的值;但是它需要被()才是表达式

C 控制语句:循环

  • 浮点数比较不要使用==
  • 非 0 即为真
  • ==时常量放在左边
  • 逗号表达式是一个序列点, 先左后右

C 控制语句:分支和跳转

  • else 与同层次的最近 if 配对, c 规定最少支持 127 层 if else
  • && ||都是序列点
  • break 只跳出一层
  • 仅在跳出多重循环时考虑使用 goto
  • stwich case 必需全是整型, 标签还得是常量

字符输入/输出和输入验证

  • 完全缓冲 (缓冲区满才刷新, 常见 512/4096 字节)与行缓冲 (遇见换行就刷新)
  • 计算机用\n^z 或者记录字节数来标记文件的结束但 c 全认为是 EOF (常见-1)
  • 用循环与验证来保证正确的输入

函数

  • 尽量单一出口
  • C 函数是平等的
  • 指针 值为地址的变量
  • 程序了解名称与值, 计算机了解地址与值

数组与指针

  • 数组可以指定初始化{[]=6}
  • 数组指定大小为变量将建立 VLA
  • 数组名是首元素的地址
  • 在形参中加 const 避免修改
  • const 与*的相对位置决定效果
  • VLA 必须是 auto 的
  • 复合字面量 (块生存周期)

字符串与字符串函数

  • 字符串字面量是创建了一个字符串常量
  • 读入字符串时留好位置
  • 命令行参数 (main 的参数)

存储类别 链接和内存管理

作用域

  • 块, 函数, 函数原型, 文件
  • 仅有 goto 标签是函数作用域
  • 仅有函数声明的变量名才有函数原型作用域 int fun(int n, int m, int[n][m]);
  • 文件作用域即全局变量 (按默认外部链接即本程序)
  • static 可以使文件作用域内部链接

存储期

  • 静态, 线程, 自动, 动态分配
  • 文件作用域拥有静态存储期 (本程序)
  • _Thread_local 声明一个对象在每个线程有一个备份
  • 块作用域有自动存储期但 static 可以让它有静态存储期
  • register 声明寄存器变量 (C 尽力)
  • 跨文件使用文件作用域的变量要用 extern 声明
  • 函数声明默认带 extern

内存形式

  • 浮点数内部全是 0 不一定值为 0
  • 静态数据在一起, 自动在一起 (栈), 动态分配在一起 (内存堆)
  • volatile 可由外部改变 (防一手编译器)
  • restrict 仅由我 (一个指针)改变 (让编译器优化)
  • _Atomic 原子性, 同时单一线程访问
  • 形参中, int* const/restrict name==int name[const/restrict], int name[static 20]表明 name 中最少元素数 (给编译器优化)

文件输入/输出

  • 文件模式与二进制模式, 文件模式会映射例如\r\n->\n
  • exit()在最初 main 中==return()
  • FILE 是一个放文件信息的结构, 其实是假结构类似 void*
  • 可以用二进制 IO 在文件中保存数据对象, 数据文件不可移植

结构和其他数据类型

  • 结构也有初始化器 struct node n={.next=NULL}
  • 结构可以自赋值
  • 伸缩型数组成员, 占用结构剩余的空间, 这种结构不能初始化
  • 常用匿名联合在结构中作为成员
  • 复杂声明()[]优先级>*

位操作

  • 掌握逻辑位运算的常见掩码套路
  • 填充位段中的"洞"
  • 使用 0 宽度的字段可以跳过这个 int 的剩下位
  • _Alignof(type)得到对齐要求 _Alignas 指定对齐要求
  • stdaligned.h->alignas alignof(与 c++相同)
  • aligned_alloc(对齐要求, 字节数) 分配内存

C 预处理器和 C 库

  • 翻译字符, 物理行转为逻辑行, 划分序列:记号, 空白, 注释, 然后用" "替换注释
  • 类对象宏与类函数宏展开
  • 不可重定义同名宏
  • 类函数宏注意替换后的运算优先级
  • #x可以在替换体中的字符串中使用 x 这一个宏参数的字符串形式
  • ##可以将记号粘合
  • 类函数宏的参数可以用... 再用__VA_ARGS__展开 #define A(x, ...) printf(#x, __VA_ARGS__)
  • getchar putchar 不是函数是宏
  • ungetc 是退回不是写入
  • 宏作用域是本文件
  • #if defined (AAA)==#ifdef AAA
  • _Generic(object, type:value, default)泛型选择
  • inline fun ();
  • _Noreturn type fun ()宣告不返回主调函数
  • atexit()将函数指针记录, exit()会逆序调用这些函数
  • assert(bool)若为假则打印 err, 停止程序 _Static_assert(bool, char*)若为假则打印字符串, 无法编译
  • type fun (type i, int parmN, ...){ va_list ap; va_start(ap, parmN); type j=va_arg(ap, type); va_end(ap); }

高级数据表示

介绍与实现了一些 ADT (抽象数据类型)与相应的简单算法

不在此记录