CPrimePlus
参考资料
- 翁恺老师的 C 语言课
- c prime plus
首先以一个轻松的视频课程了解 c, 然后...
正如它的书名所示, 它很好地介绍了 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()的读取过程 / 缓冲区中留下了什么 *在printf中是限制宽度 (读取一个参数),scanf中是跳过- 函数参数在栈中, 根据类型读取长度, 但是物理先后由实现决定
运算符, 表达式与语句
- C 标准中的数据对象 / 左值用于标识存储位置 / 对象定位值
- 除法趋零截断
- 只有共享运算对象时才考虑结合律 / 否则由编译器决定
a%b等于a-(a/b)*b- 不要在变量多次出现时使用
++--, 因为顺序不确定 - C 只保证序列点之前完成副作用
- 关于复合语句 (块), 它的值是最后一个语句的值, 但是它需要被
()才是表达式
C 控制语句: 循环
- 浮点数比较不要使用
== - 非 0 即为真
==时常量放在左边- 逗号表达式是一个序列点, 先左后右
C 控制语句: 分支和跳转
else与同层次的最近if配对, c 规定最少支持 127 层if else&&||都是序列点break只跳出一层- 仅在跳出多重循环时考虑使用
goto switch case必需全是整型, 标签还得是常量
字符输入 / 输出和输入验证
- 完全缓冲 (缓冲区满才刷新, 常见 512 / 4096 字节) 与行缓冲 (遇见换行就刷新)
- 计算机用
\n^z或者记录字节数来标记文件的结束但 C 全认为是 EOF (常见 -1) - 用循环与验证来保证正确的输入
函数
- 尽量单一出口
- C 函数是平等的
- 指针, 值为地址的变量
- 程序了解名称与值, 计算机了解地址与值
数组与指针
- 数组可以指定初始化
{[0]=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原子性, 禁止指令重排, 支持原子操作 (尽可能硬件级, 否则互斥锁), 变化同步主内存- C 语言也有内存屏障, 支持原子, 读, 写, 顺序一致, 四个等级, 但是可以不依赖原子变量直接插入
- 形参中,
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_list 类型的变量 ap
va_start(ap, parmN); // 初始化 ap, 使它指向第一个可变参数
type j=va_arg(ap, type); // 从 ap 中获取下一个参数, 类型为 type
va_end(ap); // 清理 ap, 释放资源
}
高级数据表示
介绍与实现了一些 ADT (抽象数据类型)与相应的简单算法
不在此记录