C语言系统级编程
C语言系统级编程
荣老师太帅了aaaa!!(成熟男人的魅力谁懂😭)
为什么开这门课?
”基础不牢,地动山摇“
这门课的目标
能够自己理解C语言标准中的内容
- 属于规则的一般化
- 概念组织的系统化
介绍C语言标准背后的设计思想
引例:C语言中有没有变量(variable)
1 | int a = 0; //芝士什么? 变量 |
引例2:常量 vs. 常量表达式
形式 | 名称 |
---|---|
10 | 整数常量 |
+10 | 常量表达式 |
现有学习资料和标准的概念体系有一定差异
- 核心概念介绍缺失
对象、lvalue、rvalue等核心概念介绍不详细或者不涉及 - 部分概念混淆有误
变量、常量的划分方法,常量和常量表达式的混淆等 - 存在许多自创概念
常变量、只读变量、行指针、列指针、数组指针、指针数组、多级指针、模拟引用传递
标准一般化为什么重要?
都是语法糖,没有意义?会用就行了?
如果使用变量术语,只有int a = 0;
是变量,其它定义均不知所谓
实际上,它们都是“变量标识符”
标准中的逻辑规则强调的就是一般化
一般化的基础:以对象为核心构建概念体系
- 对象的基本概念(对象的各种属性)
- 如何分配一个对象(4种分配方式)
- 如何定位一个对象(17类表达式)
- 如何获得lvalue和non-lvalue表达式的相关信息
- 如何释放对象占用的内存
本课程内容简介
C语言知识类别
- 语法类知识
- 概念类知识
- 策略类知识
学习周期和考核方法
学习时长1-9周(16学时,1学分)
考核方法:考试(开卷,带什么资料都行)+随堂测验(上课的时候带张草稿纸)
(正片开始)
内存基本概念简单回顾
1byte=8bits?
<limits.h>定义了一个宏CHAR_BIT
C语言规定一个byte等于CHAR_BIT个bit
CHAR_BIT的限制是>=8(即不一定是8位)
地址是字节在内存中的编号,只有字节才有地址
C语言最重要的概念:对象
- 对象的概念
- 如何刻画对象的属性
- 理解对象该类别
- 理解指针和数组也是一种普通的对象类型
对象表示(Object Representation)
- 一个对象各个bit组成的二进制串就是这个对象的对象表示
- 对象表示$\leftarrow$对象类型$\rightarrow$对象的值
对象表示 vs. 对象的值
- 对象表示一样,对象的值一定一样
- 对象的值一样,对象表示不一定一样
对象类型T
C语言任何一个对象类型都可以形式化定义为一个T
- sizeof(T)
规定了任何一个对象包含的字节个数,n为size,sizeof(T)=n * CHAR_BIT - alignof(T)
对象地址(Address):对象所占用的连续字节中lowest字节的编号
对齐要求(Alignment):要求对象的地址能被对齐要求整除
所有的算数类型和指针类型统称为标量类型,只有标量类型支持加减操作
对象分类——算术类型(Arithmetic Type)
C23正式增加bool类型
算术类型都是完全对象类型(即size确定)
char、signed char、unsigned char是三种不同的类型,编译器会指定char和另外两种中的一个表现相同
- 标准有符号整数类型对象
1个sign bit 若干value bit 若干padding bit
signed char、signed short int、signed int、signed long int、signed long long int
- 标准无符号整数类型对象
若干value bit 若干padding bit
unsigned char、unsigned short int、unsigned int、unsigned long int、unsigned long long int、unsigned bool
typedef
tepedef是一个能够为类型设置别名的关键字
1 | typedef T alias |
typedef可以提高系统移植能力
1 |
|
用法:
1 | typedef T alias; //非数组非指针 |
对齐要求
对象对齐要求一定是$2^n$
n越小越weak,越大约strong
枚举类型
-
在枚举定义时指定类型
1
enum Season
-
不指定类型,自动指定为char、标准有符号类型、标准无符号类型、扩展有符号证书类型、扩展无符号整数类型的一种
对象分类——派生类型(Derived Type)
重点介绍数组类型和指针类型
数组类型
形式化定义:T[N]
- T:元素类型,必须是完全对象类型
- N:元素类型的个数
N为表达式:
- 无N,不完全对象类型
- 有N,N为整数常量或整数常量表达式,普通数组类型
- 有N,不为整数常量或整数常量表达式,变长数组类型
数组类型也是一个合法的对象类型,有大小和对齐要求
typeof
数组类型可以视为一个整体,即:
1 | typedef int AINT[5]; |
由于写法过于恶臭(确实),C23提供了一个关键字
1 | typedef typeof(int[5]) AINT; |
从数组类型T继续派生
由于数组类型也为完全对象类型,所以仍可继续派生
于是出现了二维数组!
1 | int a[10][5]; |
数组都是“一维”的
指针类型
形式化定义:T*
- T:Referenced Type:对象类型或函数类型
- *:指针类型标志
T*为从T派生的类型
注意:T包括完全函数类型,也包括不完全对象类型,还包括函数类型
指针类型是完全对象类型
sizeof(T*)
alignof(T*)
指针类型再派生
指针类型也能用于构造数组类型,指针类型也有自己的指针类型
类型 | 派生数组 | 派生指针 |
---|---|---|
T | T[N] | T* |
T[M] | T[N][M] | T(*)[M] |
T* | T*[N] | T** |
你已经学会了,现在为int*(**[])[4][5]
派生指针类型吧
答案:int*(**(*)[])[4][5]
限定类型/限定符(Qualifier)
const volatile restrict
若T为类型,Q为限定符。当T为一个整体的时候,限定符在T的左边右边都一样(T Q = Q T)
1 | Q T = T Q //非数组非指针 |
理解const int* const
-
const int
,限定类型,限定int -
const int*
,指针类型,Referenced Type为const int -
const int* const
,限定类型,限定const int*
Q typeof(T[N]) = Q typeof(T)[N]
- 多个不同限定符限定同一个类型等价
- 多个同样限定符限定同一个类型等价于一个
认识对象的属性
{A, Obj_T, N, S, V, V_T, Align}
- 对象名称 T
有名称的对象被称为具名对象(Named Object) - 大小 Size
sizeof(T) - 地址
最低位地址 - 对齐要求
对齐要求:alignof(T) - 对象值和对象表示
之前讲过了喵(善用ctrl+f
) - 表示值 Value
- 表示类型 Value Type
<表示值,表示值类型>
非数组对象类型:<object value,typeof_unqual(object Type)>
数组对象类型:<第一个元素对象的地址,元素类型对应的指针类型>
分配对象:通过对象声明
分配对象的方法
rwg:建议一次只声明一个对象🤓
对象声明
- 基本形式:
T O = initalizer;
- T应当看作一个整体,即typeof(T)
int(*d)[10]; = typeof(int(*)[10]) d;
String Literal
String Literal是由编码前缀和双引号引导的字符序列构成的
编码前缀包括:
编码前缀 | 字符类型 | 编码 |
---|---|---|
无编码 | \ | \ |
u8 | char8_t | UTF-8 |
u | char16_t | UTF-16 |
U | char32_t | UTF-32 |
L | wchar_t | 实现定义 |
字符序列中的字符包括:
- 所有char,除了" \ 实际的回车
- \引导的转义字符
步骤(以初始化“hello”为例):
- 在内存中分配一个大小合适的char类型数组对象
- 用’h’,‘e’,‘l’,‘l’,‘o’,'\0’初始化数组的每一个元素
- 该对象为匿名对象
- 当第二次初始化“hello”时,不一定需要分配一个新的地址空间(如"hello"和L"hello")
String Literal vs. String
双引号内不包含"\0"的String Literal叫做String,String Literal包含String
Compound Literal
initalizier
初始化对象的对象表示
标量类型initiallizer形式:
- {},空初始化列表
- 将对象的值设置为该类型的缺省值
- 除逗号表达式之外的任意表达式epx或{exp}
数组类型initiallizer形式:
- {},空初始化列表
- 将对象的值递归设置为该类型的缺省值
- {sub-initalizer1,…}
- 设置前n个对象,剩下设置为缺省值
当对变长数组初始化时,只能使用{}进行初始化
没有initializer时,对象值初始化为indeterminate representation(不确定值)
对象值、对象表示一一对应