语言设计

编程范型

Functional Programming

函数式编程:使用纯函数

  • 程序使用紧密的函数调用表示,这些函数可以进行必要计算, 但是不会执行会改变程序状态(如:赋值)的操作
  • 函数就是数据值,可以像对待其他数据值一样对其进行操作

纯函数

纯函数:没有副作用、表达式对所有引用透明度表达式也是引用透明 的函数

  • 执行过程中除了根据输入参数给出运算结果外没有其他影响
  • 输入完全由输入决定,任何内部、外部过程的状态改变不会影响 函数执行结果
  • reference transparency:引用透明,表达式可以用其结果 取代而不改变程序含义

语言基础

错误类型

  • trapped errors:导致程序终止执行错误
    • 除0
    • Java中数组越界访问
  • untrapped errors:出错后程序继续执行,但行为不可预知, 可能出现任何行为
    • C缓冲区溢出、Jump到错误地址

程序行为

  • forbidden behaviours:语言设计时定义的一组行为,必须 包括untrapped errorstrapped errors可选
  • undefined behaviours:未定义为行为,C++标准没有做出 明确规定,由编译器自行决定
  • well behaved:程序执行不可能出现forbidden behaviors
  • ill behaved:否则

语言类型

language_types

  • strongly typed:强类型,偏向于不能容忍隐式类型转换
  • weakly typed:弱类型,偏向于容忍隐式类型转换
  • statically typed:静态类型,编译时就知道每个变量类型, 因为类型错误而不能做的事情是语法错误
  • dynamically typed:动态类型,编译时不知道每个变量类型 ,因为类型错误而不能做的事情是运行时错误
  • 静态类型语言不定需要声明变量类型

    • explicitly typed:显式静态类型,类型是语言语法的 一部分,如:C
    • implicitly typed:隐式静态类型,类型由编译时推导 ,如:ML、OCaml、Haskell
  • 类型绑定

    • 强类型倾向于值类型,即类型和值绑定
    • 弱类型倾向于变量类型,类型和变量绑定,因而偏向于 容忍隐式类型转换

polymorphism

多态:能将相同代码应用到多种数据类型上方式

  • 相同对象收到不同消息、不同对象收到相同消息产生不同动作

Ad hoc Polymorphism

ad hoc polymorphism:接口多态,为类型定义公用接口

  • 函数重载:函数可以接受多种不同类型参数,根据参数类型有 不同的行为
  • ad hoc:for this, 表示专为某特定问题、任务设计的解决 方案,不考虑泛用、适配其他问题

Parametric Polymorphism

parametric polymorphism:参数化多态,使用抽象符号代替具体 类型名

  • 定义数据类型范型、函数范型

  • 参数化多态能够让语言具有更强表达能力的同时,保证类型安全

    • C++:函数、类模板
    • Rust:trait bound
  • 在函数式语言中广泛使用,被简称为polymorphism

Subtyping

subtyping/inclsion polymorphism:子类多态,使用基类实例 表示派生类

  • 子类多态可以用于限制多态适用范围

  • 子类多态一般是动态解析的,即函数地址绑定时间

    • 非多态:编译期间绑定
    • 多态:运行时绑定
    • C++:父类指针
    • Rust:trait bound

变量设计

LvalueRvalue

  • lvaluelocation value,可寻址
  • rvaluereadable value,可读取
  • 左值:引用内存中能够存储数据的内存单元的表达式

    • 使用表达式在内存中位置
    • 考虑其作为对象的身份
  • 右值:非左值表达式

    • 使用表达式的值
    • 考虑其作为对象的内容
  • 左值、右值最初源自C
    • 左值:可以位于赋值运算符=左侧的表达式
    • 右值:不可以位于赋值运算赋=左侧的表达式

左值

  • 任何左值都存储在内存中,所以都有一个地址
  • 左值声明后,地址不会改变,地址中存储的内容可能发生 改变
  • 左值的地址是一个指针值,可以被存储在内存中的、像数据 一样被修改

特点

  • 重要原则

    • 多数情况下,需要右值处可使用左值替代
    • 需要左值处不能用右值替代
  • 重要特点

    • 左值存在在变量中,有持久的状态
    • 右值常为字面常量、表达式求职过程中创建的临时对象, 没有持久状态

一等对象

一等对象:满足以下条件的程序实体

  • 在运行时创建
  • 能赋值给变量、数据结构中的元素
  • 能作为参数传递给函数
  • 能作为函数返回结果

高阶函数

高阶函数:以其他函数作为参数、返回函数作为结果的函数

短路求值

短路求值:布尔运算and/or中若结果已经确定,则不继续计算之后 表达式

  • x and y:首先对x求值

    • x为假停止计算
    • 否则继续对y求值再判断
  • x or y:首先对x求值

    • x为真则停止计算
    • 否则继续对y求值再判断
  • 返回值取决于语言实现
    • 确定返回布尔值:C/C++
    • 返回x、或y的求值结果:python

数据模型--基本数据类型

对象、值、类型

对象:python中对数据的抽象

  • python中所有数据都是由对象、对象间关系表示

    • 按冯诺依曼“存储程序计算机”,代码本身也是由对象表示

编号、类型、值

每个对象都有各自编号类型

  • 编号:可以视为对象在内存中地址,对象创建后不变

    • id()函数:获取代表对象编号的整形
    • is算符:比较对象编号判断是否为同一对象
  • 类型:决定对象支持的操作、可能取值

    • 类型会影响对象行为几乎所有方面,甚至对象编号重要性 也受到影响,如:对于会得到新值的运算
      • 不可变类型:可能返回同类型、同取值现有对象引用
        • a = b = 1ab可能指向相同对象1 (取决于具体实现)
      • 可变类型:不允许返回已存在对象
        • c=[];d=[]:会保证cd指向不同、单独 空列表(c=d=[]将同一对象赋给cd
    • 对象创建后保持不变
    • type:返回对象类型
    • CPython:相同整形值都引用同一个对象
  • 值:通过一些特征行为表征的抽象概念

    • 对象值在python中是抽象概念

      • 对象值没有规范的访问方法
      • 不要求具有特定的构建方式,如:值由其全部数据 属性组成
    • 对象值可变性由其类型决定

      • 可变的:值可以改变的对象
      • 不可变的:值(直接包含对象编号)不可改变的对象
    • 比较运算符实现了特定对象值概念,可以认为是 通过实现对象比较间接定义对象值
  • CPython:id(x)返回存放x的地址

对象销毁

对象不会被显式销毁(del仅是移除名称绑定)

  • 无法访问时可能被作为垃圾回收

    • 允许具体实现推迟垃圾回收或完全省略此机制
    • 实现垃圾回收是质量问题,只要可访问对象不会被回收 即可
    • 不要依赖不可访问对象的立即终结机制,应当总是显式 关闭外部资源引用
  • 以下情况下,正常应该被回收的对象可能继续存活

    • 使用实现的跟踪、调试功能
    • 通过try...except...语句捕捉异常
  • CPython:使用带有(可选)延迟检测循环链接垃圾的 引用计数方案
    • 对象不可访问时立即回收其中大部分,但不保证 回收包含循环引用的垃圾

标准类型层级结构

  • 以下是python内置类型的列表,扩展模块可以定义更多类型
  • 以下有些类型有特殊属性,这些特殊属性不应用作通常使用, 其定义在未来可能改变

None

NoneType:只有一种取值,None是具有此值的唯一对象

  • 通过内置名称None访问
  • 多数情况表示空值,如
    • 未显式指明返回值函数返回None
  • 逻辑值:假

NotImplemented

NotImplementedType:只有一种取值,NotImplemented是具有 此值的唯一对象

  • 通过内置名称NotImplemented访问
  • 数值、富比较方法在操作数没有该实现操作时应返回此值
    • 返回NotImplemented前,解释器会依据运算符尝试反射 方法、委托回退方法
  • 逻辑值:真

Ellipsis

ellipsis:只有一种取值,Ellipsis是具有此值的唯一对象

  • 通过字面值...、内置名称Ellipsis访问
  • 逻辑值:真

numbers.Number

number.Number:由数字字面值创建,被作为算法运算符、算数 内置函数返回结果

  • 不可变:一旦创建其值不再改变
  • 类似数学中数字,但也受限于计算机对数字的表示方法

numbers.Integral

numbers.Integral:表示数学中整数集合

  • int:整形,表示任意大小数字,仅受限于可用内存

    • 变换、掩码运算中以二进制表示
    • 负数以2的补码表示(类似符号位向左延伸补满空位)
  • bool:布尔型,表示逻辑值真、假

    • TrueFalse是唯二两个布尔对象
    • 整形子类型:在各类场合中行为类似整形10,仅在 转换为字符串时返回"True""False"

方法、函数

  • int.bit_length():不包括符号位、开头0位长
  • int.to_bytes(length, byteorder, *, signed=False)
  • class int.from_bytes(bytes, byteorder, *, signed=False)

numbers.Real(float)

float:表示机器级双精度浮点数

  • 接受的取值返回、溢出处理取决于底层结构、python实现
  • python不支持单精度浮点
  • 没必要因为节省处理器、内存消耗而增加语言复杂度

特殊取值

1
2
3
4
5
infty = float("inf")
neg_infty = float("-inf")
# 正/负无穷大
nan = float("nan")
# Not a Number
  • 特殊取值根据定义==is肯定返回False

    • float.__eq__内部应该有做检查,保证==返回False
    • 每次会创建“新”的nan/infty
    • 连续执行id(float("nan"))返回值可能相等,这是因为 每次生成的float("nan")对象被回收,不影响
  • np.nan is np.nan返回True,应该是numpy初始化的时候 创建了一个float("nan"),每次都是使用同一个nan

相关操作

  • float.as_integer_ratio()
  • float.is_integer()
  • float.hex()
  • classmethod float.fromhex(s)
  • round(f[,n])
  • math.trunc(f)
  • math.floor(f)
  • math.ceil(f)

numbers.Complex(complex)

complex:以一对机器级双精度浮点数表示复数值

  • 实部、虚部:可通过只读属性z.realz.imag获取

Iterators

迭代器类型

  • 迭代器对象需要自身支持以下两个方法,其共同组成迭代器协议
    • iterator.__iter__()
    • iterator.__next__()
  • 方法详细参考cs_python/py3ref/cls_special_method

Generator

生成器类型:提供了实现迭代器协议的便捷形式

  • 将容器对象的__iter__()方法实现为生成器,方便实现容器对 迭代器支持
  • 创建、使用参见cs_python/py3ref/dm_gfuncs

序列

序列:表示以非负整数作为索引的有限有序集

  • 不可变序列类型:对象一旦创建不能改变

    • 若包含其他可变对象引用,则可变对象“可改变”
    • 但不可变对象所直接引用的对象集是不可变的
    • 包括
      • str
      • tuple
      • bytes
      • range:非基本序列类型
  • 可变序列:创建后仍可被改变值

    • list
    • bytesarray

通用序列操作

  • x in sx not in s

    • strbytesbytearray支持子序列检测
  • s + t:拼接

    • 拼接不可变总会生成新对象
    • 重复拼接构建序列的运行时开销将基于序列总长度乘方
  • s * nn * ss自身拼接n

    • n<0被当作0处理
    • s中项不会被复制,而是被多次引用
  • s[i]s[i:j]s[i:j:step]

    • i<0索引为负值:索引顺序相对于序列s末尾,等价于 对序列长度取模
    • 序列切片:与序列类型相同的新序列
      • 索引从0开始
      • 左闭右开
    • 某些序列支持a[i:j:step]扩展切片
  • s.index(x[, i[, j]])

    • 仅部分序列支持
    • 类似s[i:j].index(x),但返回值是相对序列开头
  • s.count(x):序列中元素x数目

  • len(s):返回序列条目数量

  • min(s)max(s):序列最小、最大值

  • 序列比较运算默认实现参见cs_python/py3ref/expressions
  • 以上运算自定义实现参见 cs_python/py3ref/cls_special_methods

不可变序列

不可变序列普遍实现而可变序列未实现的操作

  • hash()内置函数

可变序列

  • s[i]=xs[i:j]=ts[i:j:k]=t:下标、切片被赋值
    • s[i:j:k]=tt长度必须和被替换切片长度相同
  • del s[i:j]del s[i:j:k]:移除元素
    • 作为del语句的目标
    • 等同于s[i:j]=[]
  • s.append():添加元素
    • 等同于s[len(s):len(s)] = [x]
  • s.clear():移除所有项
    • 等同于del s[:]
  • s.copy():浅拷贝
    • 等同于s[:]
  • s.extend(t):扩展(合并)序列
    • 基本上等于s += t
  • s.insert(i, x):向序列中插入元素
    • 等同于s[i:i] = [x]
  • s.pop(i=-1):弹出序列中元素
  • s.remove(x):删除序列中首个值为x的项
  • s.reverse():反转序列
    • 反转大尺寸序列时,会原地修改序列
    • 为提醒用户此操作通过间接影响进行,不会返回反转后序列
  • arraycollections模块提供额外可变序列类型
  • 可利用collections.abc.MutableSequence抽象类简化自定义 序列操作

tuple

元组

  • 元组中条目可以是任意python对象
  • 元组创建
    • 一对圆括号创建空元组
    • 逗号分隔
      • 单项元组:后缀逗号a,(a,)
      • 多项元组:a,b,c(a,b,c)
    • 内置构建器:tupletuple(iterable)

list

列表

  • 列表中条目可以是任意python对象
  • 构建方式
    • 方括号括起、项以逗号分隔:[][a][a,b]
    • 列表推导式:[x for x in iterable]
    • 类型构造器:list(iterable)

相关操作

.sort
1
2
def list.sort(*, key=None, reverse=False):
pass
  • 用途:对列表原地排序

    • 使用<进行各项之间比较
    • 不屏蔽异常:若有比较操作失败,整个排序操作将失败, 此时列表可能处于部分被修改状态
  • 参数

    • key:带参数函数,遍历处理每个元素提取比较键
      • None:默认,直接使用列表项排序
  • 说明

    • .sort保序,有利于多重排序
    • 为提醒用户此方法原地修改序列保证空间经济性,其不返回 排序后序列(可考虑使用sorted显式请求)
  • CPython:列表排序期间尝试改变、检测会造成未定义影响, CPython将列表排序期间显式为空,若列表排序期间被改变将 raise ValueError

str

1
2
3
4
5
class str(object="")
# 返回`object.__str__()`、`object.__repr__()`
class str(object=b"", encoding="utf-8", errors="strict")
# 给出`encoding`、`errors`之一,须为bytes-like对象
# 等价于`bytes.decode(encoding, errors)`

字符串:由Unicode码位值组成不可变序列(应该是UTF16-bl编码)

  • 范围在U+0000~U+10FFFF内所有码位值均可在字符串中使用
  • 不存在单个“字符”类型
    • 字符串中单个字符为长度为1字符串
  • 不存在可变字符串类型
    • 可以用str.join()io.StringIO高效连接多个字符串 片段
  • 字符串构建
    • 字符串字面值:cs_python/py3ref/lexical_analysis
    • 内置构造器str()

相关操作

  • ord():转换单个字符字符串为(整形)码位
  • chr():转换(整形)码位为单个字符字符串
判断
  • str.isalnum()
  • str.isalpha()
  • str.isascii()
  • str.isdecimal()
  • str.isdigit()
  • str.isidentifier()
  • str.islower()
  • str.isnumeric()
  • str.isprintable()
  • str.isspace()
  • str.istitle()
  • str.isupper()
查找
  • str.rfind(sub[, start[, end]])
  • str.rindex(sub[, start[, end]])
  • str.startswith(prefix[, start[, end]])
  • str.endwith(suffix[, start[, end]])
  • str.count(sub[, start[, end]]):子串出现次数
  • str.find(sub[, start[, end]])
    • 仅检查sub是否为子串,应使用in
    • 找不到子串时返回-1
  • str.index(sub[, start[, end]])
    • 类似str.find,但找不到子串时raise ValueError
分隔
  • str.partition(sep)
  • str.rpartition(sep)
  • str.rsplit(sep=None, maxsplit=-11)
  • str.split(sep=None, maxsplit=-1)
  • str.splitline([keepends])
拼接
  • str.join(iterable)
  • str.strip([chars])
  • str.lstrip([chars])
  • str.rstrip([chars])
  • str.rstrip([chars])
转换
  • str.lower()
  • str.upper()
  • str.swapcase()
  • str.translate(table)
  • str.replace(old, new[, count])
  • static str.maketrans(x[, y[, z]])
  • str.encode(encoding="utf-8", errors="strict"):使用 指定编码方案编码为bytes
  • str.expandtabs(tabsize=8)
  • str.capitalize():首字符大写副本
  • str.casefold():消除大小写副本
  • str.center(width[, fillchar]):字符串位于中间的字符串
  • str.title()
格式化
  • str.ljust(width[, fillchar])
  • str.rjust(width[, fillchar])
  • str.zfill(width)
  • str.format(*args, **kwargs)
  • str.format_map(mapping)
    • 类似str.format(**mapping),但mapping不会被复制 到dict
      1
      2
      3
      4
      class Default(dict):
      def __missing__(self, key):
      return key
      "{name} was born in {country}".format_map(Default(name="Guido"))
printf风格字符串格式化
  • format % values中:format%转换标记符将被转换 为values中条目

    • 效果类似于sprintf
    • values为与format中指定转换符数量等长元组、或映射 对象,除非format要求单个参数
  • 转换标记符按以下顺序构成

    • %字符:标记转换符起始
    • 映射键:可选,圆括号()括起字符序列
      • values为映射时,映射键必须
    • 转换旗标:可选,影响某些类型转换效果
      • #:值转换使用“替代形式”
      • 0:为数字值填充0字符
      • -:转换值左对齐(覆盖0
      • :符号位转换产生整数(空字符串)将留出空格
      • +:符号字符显示在开头(覆盖
    • 最小字段宽度:可选
      • *:从values读取下个元素
    • 精度:可选,.之后加精度值
      • *:从values读取下个元素
    • 长度修饰符:可选
    • 转换类型
      • d/u/i:十进制整形
      • o:8进制整形
        • #替代形式,前端添加0o
      • x/X:小/大写16进制整形
        • #替代形式,前端添加0x/0X
      • e/E:小/大写浮点指数
        • #替代形式,总是包含小数点
      • f/`F:浮点10进制
        • #替代形式,总是包含小数点
      • g/G:指数小于-4、不小于精度使用指数格式
        • #替代形式,总是包含小数点,末尾0不移除
      • c:单个字符(接收整数、单个字符字符串)
      • r/s/a:字符串(repr/str/ascii转换)
        • 按输出精度截断
      • %:输出%字符

技巧

  • 快速字符串拼接
    • 构建包含字符串的列表,利用str.join()方法
    • 写入io.StringIO实例,结束时获取值

bytes/bytearray

1
class bytes([source[, encoding[, errors]]])
  • 字节串:单个字节构成的不可变序列
  • 字节数组:字节串可变对应版本,其他同不可变bytes
  • 字节串构建

    • 字节串字面值:cs_python/py3ref/lexical_analysis
    • 内置构造器bytes()
      • 指定长度零值填充:bytes(10)
      • 整数组成可迭代对象:bytes(range(20))
      • 通过缓冲区协议复制现有二进制数据:bytes(obj)
  • 字节数组构建

    • 字节数组没有字面值语法,只能通过构造器构造
    • 可变,构建空字节数组有意义
  • 类似整数构成序列

    • 每个条目都是8位字节
    • 取值范围0~255,但只允许ASCII字符0~127
    • b[0]产生整数,切片返回bytes对象
    • 可通过list(bytes)bytes对象转换为整数构成列表
  • memeoryview提供支持

相关函数、方法

  • bytes.decode:解码为相关字符串
  • classmethod bytes.fromhex(string)
  • bytes.hex()

技巧

  • 快速字节串拼接
    • 构建包含字节串的列表,利用bytes.join()方法
    • 写入io.BytesIO实例,结束时获取值
    • 使用betaarray对象进行原地拼接

memoryview

1
class memoryview(obj)

内存视图:允许python代码访问对象内部数据

  • 若对象支持缓冲区协议,则无需拷贝

    • 支持缓冲区协议的内置对象包括bytesbytesarray
  • 内存视图元素:原始对象obj处理的基本内存单元

    • 对简单bytesbytesarray对象,一个元素就是一字节
    • array.array等类型可能有更大元素
  • 内存视图支持索引抽取、切片

    • 若下层对象可选,则支持赋值,但切片赋值不允许改变大小

相关操作

  • mv.__eq__(exporter)
  • mv.__len__()
  • mv.tobyte()
  • mv.hex()
  • mv.tolist()
  • mv.release()
  • mv.cast(format[, shape]):将内存视图转换新格式形状

可用属性

以下属性均只读

  • mv.obj:内存视图的下层对象
  • mv.nbytes
    • == product(shape) * itemsize = len(mv.tobytes())
  • mv.readonly
  • mv.format:内存视图中元素格式
    • 表示为struct模块格式
  • mv.itemsize
  • mv.ndim
  • mv.shape
  • mv.strides
  • mv.suboffsets
  • mv.c_contiguous
  • mv.f_contiguous
  • mv.contiguous

Slices Object

切片对象:表示__getitem__()方法得到的切片

  • 可以使用内置的slice()函数创建
  • a[start: stop]形式的调用被转换为 a[slice(start, stop, None)]
  • 切片对象是内部类型,参见cs_python/py3ref/dm_exec,也 不是序列类型

特殊只读属性

  • start:下界
  • stop:上界
  • step:步长值
  • 属性可以具有任意类型

方法

  • .indices(self, length):计算切片对象被应用到length 长度序列时切片相关信息
    • 返回值:(start, stop, step)三元组
    • 索引号缺失、越界按照正规连续切片方式处理

range

range:不可变数字序列类型(非不是基本序列类型)

1
2
class range(stop)
class range(start=0, stop[, step=1])
  • 参数:必须均为整数(int或实现__index__方法)

    • step > 0:对range对象r[i]=start + step * i,其中 i >= 0, r[i] < stop
    • step < 0:对range对象r[i]=start + step * i,其中 i >= 0, r[i] > stop
    • step = 0raise ValueError
  • 说明

    • 允许元素绝对值大于sys.maxsize,但是某些特性如: len()可能raise OverflowError
    • range类型根据需要计算单项、切片值
      • 相较于常规listtuple占用内存较小,且和表示 范围大小无关
      • 只能表示符合严格模式的序列
    • range类型实现了collections.abc.Sequence抽象类
      • 基本实现序列所有操作:检测、索引查找、切片等
      • 除拼接、重复:拼接、重复通常会违反严格模式
    • !===range对象视为序列比较,即提供相同值即 认为相等

集合类型

  • 表示不重复不可变对象组成的无序、有限集合

    • 不能通过下标索引
    • 可以迭代
    • 可以通过内置函数len返回集合中条目数量
  • 常用于

    • 快速成员检测、去除序列中重复项
    • 进行交、并、差、对称差等数学运算

公用操作

  • len(s)
  • x [not ]in s
  • s.isdisjoint(other)
  • s.issubset(other)/s <= other
  • s < other
  • s.issuperset(other)/s >= other
  • s > other
  • s.union(*others)/s | other |...
  • s.intersection(*others)/s & other &...
  • s.difference(*other)/s - other - other
  • s.symmetric_difference(other)/s ^ other
  • s.copy()
  • 集合比较仅定义偏序,集合列表排序无意义

可变集合独有

  • s.update(*others)/s |= other |...
  • s.intersection_update(*others)/s &= other &...
  • s.difference_udpate(*others)/s -= other |...
  • s.symmetric_difference_update(other)/set ^= other
  • s.add(elem)
  • s.remove(elem)
  • s.discard(elem)
  • s.pop()
  • s.clear()

set/frozenset

1
2
class set([iterable])
class frozenset([iterable])
  • 集合:由具有唯一性的hashable对象组成的多项无序集
  • 冻结集合:不可变集合,可哈希,可以用作集合元素、字典键
  • 创建集合

    • set()内置构造器
    • 花括号包括、逗号分隔元组列表:{a, b}
  • 创建冻结集合

    • frozenset()内置构造器
  • python中集合类似dict通过hash实现

    • 集合元素须遵循同字典键的不可变规则
    • 数字:相等的数字1==1.0,同一集合中只能包含一个

操作说明

  • .remove.__contains__discard等可以接收set类型 参数,其将被转换为临时frozenset对象

  • 非运算符版本操作可以接受任意可迭代对象作为参数,运算符 版本只能接受集合类型作为参数

映射

映射:表示任何索引集合所索引的对象的集合

  • 通过下标a[k]可在映射a中选择索引为k的条目
    • 可在表达式中使用
    • 可以作为赋值语句、del语句的目标
  • dbm.ndbmdbm.gnucollections模块提供额外映射类型

通用操作

  • len(d)
  • d[key]
  • key [not ]in d
  • iter(d)
  • d.keys():返回字典视图对象
  • d.values():返回字典视图对象
  • d.items():返回字典视图对象
  • d.get(key[, default])
  • d.copy()
  • classmethod fromkey(iterable[, value])

可变映射独有

  • d[key]=value
  • del d[key]
  • d.clear()
  • d.setdefault(key[, default])
  • d.pop()
  • d.popitem()
  • d.copy()
  • d.update()

dict

1
2
3
class dict(**kwargs)
class dict(mapping, **kwargs)
class dict(iterable, **kwargs)

字典:可由几乎任意值作为索引的有限个对象可变集合

  • 字典的高效实现要求使用键hash值以保持一致性

    • 不可作为键的值类型
      • 包含列表、字典的值
      • 其他通过对象编号而不是值比较的可变对象
    • 数字:相等的数字1==1.0索引相同字典条目
  • 创建字典

    • 花括号括起、逗号分隔键值对:{key:value,}
    • 内置字典构造器:dict()

字典视图对象

字典视图对象:提供字典条目的动态视图,随字典改变而改变

  • len(dictview)
  • iter(dictview)
  • x in dictview

C++ 概述

C++ 程序结构

  • C++ 是面向对象和过程的范式的集合

Comment

注释:被编译器忽略的文本

  • /**/:允许多行注释
  • //:单行注释

Library

库:提前编写的能执行有用操作工具集合

库引入

  • #include <>:头文件是C++标准中的系统库
  • #include ".h":其他库、自行编写的头文件

Namespace

命名空间:被切分为代码段的结构,为确保定义在大系统中各部分 程序的元素名称(如:变量名、函数名等)不会相互混淆

  • std:C++标准库的命名空间
  • 需要告诉编译器定义所属的命名空间才能引用头文件中的名称

函数

为方便理解,大多数程序被划分为几个较小的易于理解的函数

  • Prototype:函数原型,函数定义的首行加上分号结尾组成
  • 主程序:每个C++程序必须有main函数,指明程序计算 的开始点,main函数结束时,程序执行也随之结束

Variable

变量:一个命名的、能够存储特定类型值的一块内存区域

  • 在变量生存期内,所有变量的名字、类型都是不改变的, 而变量的值一般会随着程序运行发生改变

  • 可以将变量视为盒子

    • 变量名:作为和盒子的标签在盒子外
    • 变量值:在盒子里物品
  • C++/C中变量的解释是由变量类型而不是决定, 存储值一样,可能会有不同的处理方式

    • 引用、指针都存储地址,但是引用可以直接作为变量使用, 指针则需要解引用
  • initializer:初始化,初始值作为声明的一部分
  • 变量类型、修饰符参见cs_cppc/basics/mem_ctl

Scope

变量作用域

Local Variable

局部变量:声明在函数体内,作用域可以扩展到其 声明所在块

  • 函数被调用时:为每个局部变量分配在整个函数调用时期 的存储空间
  • 函数返回时:所有局部变量消亡
  • 局部变量一般存储在函数栈中

Global Variable

全局变量:声明在函数定义外,作用域为其声明所在文件

  • 生命期为程序运行的整个运行期,可用于存储函数调用的值
  • 可以被程序中任意函数操作,难以避免函数间相互干扰
  • 除声明全局常量外,不采用全局变量易于管理程序
  • 全局变量一般存储在静态区中

Declare

声明:主要功能是将变量的名字和变量包含值类型相关联

  • 在使用变量之前必须声明
  • 变量声明在程序中的位置决定了变量的scope
  • 事实上,函数、类等都可以提前声明,有些时候必须如此,以 使用forward reference(前向引用,定义之前声明指向其的 指针)

Identifier

标识符:变量、函数、类型、常量等名字的统称

  • 必须以字母、_开始
  • 所有字符必须是字母、数字、_,不允许空格或其他特殊字符
  • 不能包含保留字
  • 标识符中大小写字母是不同的
  • 标识符可以取任意长度,但是C++编译器不会考虑任何超过31个 字符的两个名字是否相同

Shadowing

遮蔽:程序代码内层块中变量隐藏外层块中同名变量的行为

隐式类型转换

数值类型转换

  • 提升型转换:通常不会造成数值差异

    • 小整数charshort转换到int
    • float转换到double
  • 可能存在转换误差的转换

    • 负数转换为无符号类型:二进制位不变,即为负数对应补码 正数
    • 其他类型转换为bool类型
    • 浮点数转换为整数:截断,若出现数值溢出,则出现未定义 行为

指针类型转换

  • 空指针void *、任意指针类型类型之间相互转换
  • 衍生类指针转换为基类指针,同时不改变constvolatile 属性
  • C风格数组隐式把数组转换为指向第一个元素的指针
    • 容易出现错误
      1
      2
      3
      char * s = "Help" + 3;
      # ”Help"被转换为指向数组指针,向后移3位
      # `s`指向最后元素`p`

强制类型转换

  • 上行转换:派生类指针、引用转换为基类表示
  • 下行转换:基类指针、引用转化为派生类表示

const_cast

const_cast:去掉原有类型的constvolatile属性,将常量 指针、引用转换为非常量

  • 常量指针转化为非常量指针,仍来指向原来对象
  • 常量引用转换为非常量引用,仍然指向原来对象
  • 一般用于修改指针,如const char *p
  • 要求期望去常量目标非常量,否则为未定义行为
1
2
3
4
5
6
7
8
9
10
11
int ary[4] = {1, 2, 3, 4};
// 去常量化目标非常量
const int * c_ptr = ary;
// 常量化数组指针
// 不能直接数组中值
int * ptr = const_cast<int*>(c_ptr);
// 去`const`,强制转换为非常量化指针
// 可以修过数组中值
for(int i = 0; i < 4; i++){
ptr[i] += 1;
}
未定义行例
  • 堆区常量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    int con_cast(){
    const int * c_val_ptr = new const int(1);
    // 常量值,非常量指针
    int vec_before[*c_val_ptr];
    // 常量声明数组
    int & ptr_val = const_cast<int &>(*c_val_ptr);
    ptr_val += 1;
    // 可以正常给值加1
    // 未定义行为?堆区不存在常量?
    int vec_after[*c_val_ptr];
    // 常量生命数组
    cout << sizeof(vec_before) << endl <<
    sizeof(vec_after) << after;
    // 二者长度均为`8`,即常量在`vec_before`创建前已处理
    cout << *c_val_ptr << endl << ptr_val << endl
    << c_val_ptr << endl << &ptr_val;
    // 地址、值均相同
    }
  • 栈区常量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    int con_cast(){
    const int c_val= 1;
    // 常量值
    int vec_before[c_val];
    // 常量声明数组
    int & ptr_val = const_cast<int &>(&c_val);
    ptr_val += 1;
    // 可以正常给值加1
    int vec_after[c_val];
    // 常量生命数组
    cout << sizeof(vec_before) << endl <<
    sizeof(vec_after) << after;
    // 二者长度均为`4`,即常量值已处理,但没有改变
    cout << c_val << endl << ptr_val << endl
    << &c_val<< endl << &ptr_val;
    // 地址保持相同、但二者值不同
    }
  • 以上代码在g++4.8.5中测试

static_cast

static_cast:静态类型转换,无条件转换

  • 类层次中基类、派生类之间指针、引用转换

    • 上行转换:派生类完全包含基类所有成员,安全
    • 下行转换:派生类包含独有成员,没有动态类型检查,对象 为派生类实例时转换不安全
    • 基类、派生类之间转换建议使用dynamic_cast
  • 基本类型转换:安全性需要开发者维护

    • intcharenumfloat之间相互转换
    • 空指针转换为目标类型指针:不安全
    • 任何类型表达式转换为void类型
  • 不能进行无关类型指针(无继承关系、float与int等)之间 转换,而C风格强转可以
  • 不能转换掉原有类型的constvolatile__unaligned 属性
  • 静态是相对于动态而言,只在编译时检查,编译时已经确定转换 方式,没有运行时类型检查保证转换安全性
1
2
3
4
5
6
7
8
9
10
11
12
float f_pi = 3.141592f
int i_pi = static_cast<int>(f_pi)

Sub sub;
// 衍生类
Base * base_ptr = static_cast<Base*>(&sub);
// 上行转换,安全

Base base;
// 基类
sub * sub_ptr = static_cast<Sub*>(&base);
// 下行转换,不安全
  • 和C风格强转效果基本一致(使用范围较小),同样没有运行时 类型检查保证转换安全性,有安全隐患
  • C++中所有隐式转换都是使用static_cast实现

dynamic_cast

dynamic_cast:指针、引用动态类型转换,有条件转换

  • 安全的基类、派生类之间转换

    • 转型对象为指针:转型失败返回NULL
    • 转型对象为引用:转型失败抛出异常
  • 动用runtime type information进行类型安全检查,会有效率 损失

    • 依赖虚函数表将基类指针转换为子类指针???
    • 检查对象实例类型,保证转换是安全的,不会出现 子类指针指向父类对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Base{
public:
void print(){
cout << "i' base" << endl;
}

virtual void virtual_foo() {};
}

class Sub: public Base{
public:
void print(){
cout << "i'm sub" << endl;
}
virtual void virtual_foo();
}

int main(){
cout << "Sub -> Base" << endl;
Sub * sub = new Sub();
sub -> print();
// 打印:`i'm sub`
Base * sub2base = dynamic_cast<Base*>(sub);
if (sub2base != NULL)l{
sub2base->print();
// 打印:`i'm base`
}
cout << "sub2base val: " << sub2base << endl;

cout << "Base -> Sub" << endl;
Base * base = new Base();
base->print();
// 打印:`i'm base`
Sub * base2sub = dynamic<Sub*>(base);
if (base2sub != NULL){
base2sub -> print();
// 未打印
}
count << "base2sub val: " << base2sub << endl;

delete sub;
delete base;
return 0;
}
  • 涉及面向对象的多态性、程序运行时的状态,主要是用于虚类 类型上行转换
  • 同编译器的属性设置有关,所以不能完全使用C语言的强制转换 替代,常用、不可缺少

reinterpret_cast

reinterpret_cast:仅仅是重新解释给出对象的比特模型 ,没有对值进行二进制转换

  • 用于任意指针、引用之间的转换
  • 指针、足够大的整数(无符号)之间的转换
1
2
3
4
5
6
int * ptr = new int(233);
uint32_t ptr_addr = reinterpret_cast<uint32_t>(ptr);
count << "ptr addr: " << hex << ptr << endl
<< "ptr_add val: " << hex << ptr_addr << endl;
// 二者输出值相同
delete ptr;
  • 处理无关类型转换,通常为位运算提供较低层次重新解释
  • 难以保证移植性

C风格

1
2
3
4
5
6
7
8
9
10
Typename b = (Typename) a;

float b = 1.0f;
int c_i = (int)b;
int & c_j = (int&)b;
// C风格

int cpp_i = static_cast<int>(b);
int & j = reinterpret_cast<int&>(b);
// 等价C++风格
  • 没有运行时类型检查保证转换安全性,可能有安全隐患

Data Type

数据类型:从形式上看,数据类型有两个属性定义

  • domain:值集,该类型值的集合
  • set of operation:操作集,定义类型的行为
  • C++每个数据值都有其相应数据类型
  • Primitive Type:基本类型,类型系统整体的建筑块
    • 整型
    • 浮点型
    • 布尔型
    • 字符
    • 枚举类型

Integer

C++定义了3种整数类型:shortintlong

  • 由值域大小相互区别

值域

  • C++中没有指定3种类型确切值域,其取决于机器、编译器,但是 设计者可以更确切的定义各整形值域

    • shortintlong类型内存不减
    • int类型最大值至少$2^{15}-1$
    • long类型最大值至少$2^{31}-1$
  • 一般的

    • short:2bytes
    • int:4bytes
    • long/long long:8bytes
  • 若希望明确值域,尝试<cstdint>中自定义类型

unsigned

  • 各整形均可以在其类型之前加上关键字unsigned,构建新的 非负整形

  • 无符号整型可以提供有符号整型两倍正值域

  • 16进制、8进制等都是无符号输出格式,有符号整形会被 隐式转换位无符号(若传入)

表示

  • 整形常量一般写成十进制数字
  • 数字以0开始:编译器将其视为八进制数字
  • 数字以0x开始:编译器将其视为16进制数字
  • 数字L结尾:显式指明整数常量的类型为long(表达式中)
  • 数字U结尾:整形常数被认为时无符号整数(表达式中)

Float-Point

C++中定义了3种浮点类型:floatdoublelong double

值域

C++同样没由指定这些类型的确切表示

  • floatdoublelong double占用内存不减、精度 不减

表示

  • 通常使用带有小数点的十进制数字
  • 支持科学计数法风格:浮点数乘以十的整数幂

Bool

布尔类型:具有合法常量值truefalse的数据类型

Char

C++中表示字符的预定义基本类型:char

  • C++标准库定义wchar_t类型表示宽字符以扩展ASCII编码范围

值域

出现在屏幕、键盘上的字母、数字、空格、标点、回车等字符集合

  • 在机器内部,这些字符被表示成计算机赋给每个字符的数字代码
  • 多数C++实现中,表示字符的代码系统为ASCII

表示

  • '':单引号括起的一个字符表示字符常量
  • escape sequence:转移序列,以\开始的多个字符表示 特殊字符

对比整形

  • 整形没有1byte大小类型,很多情况下使用char类型存储 整数值以节省空间

  • 但C/C++某些[unsigned ]char、整形处理有区别

输入、输出
  • [unsigned ]char类型总是输出字符,而不是数字串

    • 输出流:关于整形的流操纵符对[unsigned ]char无效, 即使是无符号类型
    • 格式化输出:指定输出格式得到数字串,包含隐式类型转换

String

字符串:字符序列

  • C风格的字符串就是以\0结尾的字符数组
  • \0null character,空字符,对应ASCII码为0

表示

  • "":双引号括起的字符序列表示字符串常量
  • 允许使用转移序列表示字符串中特殊字符
  • 两个、两个以上字符串连续出现在程序中,编译器会自动将其 连接(即可以将字符串分行书写)

Enumerated

枚举类型:通过列举值域中元素定义的新的数据类型

1
enum typename { namelist };

值域

  • 默认的,编译器按照常量名顺序,从0开始给每个常量赋值
  • 允许给每个枚举类型常量显式的赋值
  • 若只给部分常量名赋值,则编译器自动给未赋值常量赋最后一个 常量值后继整数值

复合类型

基于已存在的类型创建的新类型

表达式

C++中表达式由项、操作符构成

  • term:项,代表单个数据的值,必须是常量、变量、函数调用
  • operator:操作符,代表计算操作的字符(短字符序列)
    • binary operator:二元操作符,要求两个操作数
    • unary operator:一元操作符,要求一个操作数
  • full expression:完整表达式,不是其他表达式子表达式 的表达式

表达式求值顺序

C++没有规定表达式求值顺序(表达式求值顺序是指CPU计算 表达式的顺序,不同于优先级、结合律)

  • sequenced before:若A按顺序先于B,A中任何计算都 先于B中任何计算
  • sequenced after:若A按顺序后于B,A中任何计算都晚于 B中任何计算
  • unsequenced:若A与B无顺序,则A、B中计算发生顺序 不确定,并且可能交叉
  • indeterminately sequenced:若A与B顺序不确定,则 A、B计算发生顺序不确定,但不能交叉
  • 对无顺序、顺序不确定求值,不要求两次不同求值使用相同顺序
  • 完整表达式的求值、副作用先于下个完整表达式的求值、 副作用

  • 表达式中不同子表达式的求值无顺序

    • 除非特殊说明,运算符的不同操作数求值是无顺序的
    • 运算操作数值计算先于运算符结果值计算
  • 对同一简单对象

    • 两个不同副作用无顺序,则为无定义行为
    • 副作用与需要此对象值的计算无顺序,则为无定义行为
    1
    2
    3
    int i = 0;
    i++ + i++; // 两`i++`对i`均副作用、无顺序,未定义行为
    i + i++; // `i++`副作用、`+`计算无顺序,未定义行为
  • 函数调用时:不仅限于显示函数调用,包括运算符重载、构造、 析构、类型转换函数

    • 实参求值、副作用先于函数体任何语句、表达式求值

    • 函数不同实参求值、副作用无顺序

      1
      2
      3
      int func(int, int);
      int i = 0;
      func(i++, i++); // 参数计算`i++`对`i`均有副作用,无定义行为
    • 主调函数中任何既不先于、也不后于被调函数的求值,其 与被调用函数都是顺序未指定,即主调函数中任何求值 与被掉函数不交叉

      1
      2
      3
      4
      int foo(int);
      int i = 0, j = 0, k = 0;
      (i++ + k) + foo(j++);
      (i++ + k) + foo(i++);
    • 不同形参初始化是顺序未指定

  • 自增、自减

    • 后缀形式:i++i--
      • 值计算先于对变量的修改
      • 与其顺序未指定函数调用不能插入值计算、变量修改 之间
    • 前缀形式:++i--i
      • 返回被更新之后的操作数(左值)
      • i非布尔值时,++i--i等价于i+=1i-=1
  • new

    • 内存分配函数与初始化参数求值顺序未指定
    • 新建对象初始化先于new表达式计算
  • 逻辑与&&、或||为短路求值

    • 左操作数计算先于右操作数计算
    • 左操作数为falsetrue时,右操作数不会被求值
  • ?:中三个操作数只有两个会被求值

    • 第一个操作数求值先于后两个操作数求值
    • 第一个操作数值为true时,第二个操作数被求值,否则 第三个操作数被求值
  • 赋值运算符=

    • 左、右操作数求值先于赋值操作
    • 赋值操作先于赋值表达式值计算
    • 赋值表达式返回其左操作数的左值,此时左操作数必然被 赋值
  • 复合赋值运算符e1 op= e2+=-=

    • 求值包括e1 op e2、结果赋给e1、返回e1
    • 任何函数调用不能插入以上步骤
  • 逗号,运算符(注意区分逗号分隔符)

    • 左操作数值被丢弃
    • 左操作数值计算、副作用先于右操作数值计算、副作用
    • 被重载后的逗号运算符将生成函数调用,对操作数求值遵循 函数实参求值顺序
  • 序列初始化:对{}存在的多个初始化参数求值

    • 初始化参数值计算、副作用先于被逗号分隔的后初始化值 计算、副作用
    • 即使初始化参数引起函数调用,列表每个值作为函数参数, 求值顺序仍然被保留

优先级、结合律

  • precedence:优先级,默认情况下(无括号)操作符在运算中 结合的方法

  • associativity:结合律,相同优先级的运算符运算顺序, 也即左右操作数的运算顺序

    • left-associative:左结合的,优先计算操作符左侧 表达式,大部分操作符时左结合的
    • right-assiciative:右结合的
优先级递减 结合性
()[]->.
一元操作符:-+--++!&*~(类型)sizeof
*/%
+-
>><<(右、左移位)
<<=>>=
==!=
&
^
` `
&&
` `
?:
=op=
  • 操作符只是语言的语法,其行为只是人为赋予的规则,其行为 可能符合逻辑,也可能不符合逻辑

混合类型

对于具有不同操作数的操作符,编译器会将操作数转化为其中精度 最高的类型,计算结果也为精度最高的类型,保证计算结果尽可能 精确

整数除法、求余

  • 两个整数除法运算:结果为整数,余数(小数)被舍去
  • 含负数操作数的除法、求余:依赖硬件特征
    • 求余一般返回同余正值

Type Cast

(值/静态)类型转换:将一种类型明确的转换为另一种类型

1
2
3
4
type(expr)
# C++风格转换
(type)expr
# C风格类型转换
  • 转换目标类型精度增加不丢失信息,否则可能会丢失信息

  • 符号整形转换无符号整形:依赖硬件特征,一般

    • 符号位置于最高位
    • 数值位根据精度
      • 同精度:不变,即包括符号位在内无改变
      • 低精度向高精度:高位用符号位补齐
      • 高精度向低精度:截断、保留低位
  • 无符号整形转符号整形

    • 同精度、高精度向低精度:截断、保留低位
    • 低精度向高精度:高位补0
  • 即:有符号转为其他类型(有符号、无符号),优先保留符号位

赋值操作

C++中,对变量的赋值是一种内置的表达式结构

  • 赋值操作符=要求其左操作数必须是可变的,通常是变量名

  • 首先计算赋值操作符右边表达式值,再赋给左边的变量

    • 右操作数可能需要进行类型转换以使其与左操作数的类型 相匹配
  • 赋值操作默认(未重载)是通过将源对象所有变量域(栈中 数据),复制到目标对象相应变量域实现的

返回值

C++赋值表达式返回右边表达式的值

  • 可以被组合进更大表达式中(但会影响阅读)
  • multiple assignment:多重赋值,可以方便给多个变量赋 相同值

Shorthand Assignment

将赋值操作符、二元操作符相结合产生形式

1
2
3
var op= expr;
var = var op expr;
// 等价

自增、自减

对变量进行+1-1更高级别的缩写形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
x++;
// 后缀形式
// 自增前将其原始值返回给临近表达式
++x;
// 前缀形式
// 自增后将新值返回给临近表达式
x += 1;
x = x+1;
// 等价
y--;
--y;
y -= 1;
y = y-1;
// 等价

布尔运算

Relational Operator

关系操作符

  • ==:等于,容易犯错
  • !=:不等于
  • >:大于
  • <:小于
  • >=:大于等于
  • <=:小于等于

Logical Operator

逻辑操作符:采用布尔类型操作数,组合形成新的布尔值

  • !:逻辑非
  • &&:逻辑与
  • ||:逻辑或
Short-Circuit Evaluation

短路求值:得到结果时就立刻结束计算表达式

  • 依赖于:3种逻辑操作符优先级均不同,逻辑运算表达式总是 从左到右计算的

?:

三目操作符,需要3个操作数

1
2
3
(condition) ? expr_1 : expr_2
// `()`不必须,只是用于强调边界
// 首先计算`condition`,条件为`true`则返回`expr_1`

Bitwise Operator

位运算符:读取任意标量类型值,将其翻译成与底层硬件相应的 比特序列表示

  • &|^:位逻辑与、或、异或
  • ~:位逻辑非
  • >><<:右、左移位
    • 无符号数:字尾被移动比特数消失,另一端补0
    • 有符号数:行为依赖于硬件特征,一般保证乘除特性
      • 右移:补1
      • 左移:补0

语句

  • simple statement:简单语句,执行某些动作

    • 表达式加分号组成
  • control statement:控制语句,控制程序流程

    • 控制语句典型地应用在一条单一语句

Block

块:{}括起指明一组语句序列是连贯单元的一部分

  • 编译器会将整个块当作一条语句对待,也被称为 compound statement
  • 常用于使用特定控制语句控制一组语句

Conditional Execution

条件执行:根据检测条件控制程序后续执行

if

1
2
if (condition) statement
if (condition) statement else statement
  • if中控制语句可以是一条简单语句,也可以是一个语句块

switch

1
2
3
4
5
6
7
8
9
10
11
12
13
switch (e){
// `e`:*control expression*
case c1:
//`c1`必须是常量标量
statements
break;
case c2:
statements
break;
default:
statements
break;
}
  • 程序计算控制表达式e的值,将结果同c1c2相比较

    • case后的常量必须是标量类型,即底层采用整数表示 的类型,如:整形、字符、枚举类型
  • 如果常量同控制表达式值相匹配,则跳转至相应case子句执行

    • 执行到子句中break时跳出switch语句
    • 若子句中无break,则接着执行之后case子句中语句, 直到遇到break/return跳出switch语句,这会带来很多 问题,除
      1
      2
      3
      4
      case 1:
      case 2:
      statement
      break;
  • default可选,执行没有和控制表达式匹配值的操作

    • 除非确定列举了所有可能情况,否则增加default子句是 好习惯

Iterative Statement

迭代语句:以循环的方式多次执行程序中的一部分

while

一般模式
1
2
3
while (condition-expression){
statements
}
  • 首先查看条件表达式值
  • 若条件表达式值为true,整个循环体被执行,然后返回到循环 开始检查条件表达式值
  • 若条件表达式值为false,则循环终止
  • 每个循环周期,包括第一次循环,条件表达式都会被测试,且 仅在循环开始进行,循环中间条件表达式值改变不会被注意
Read-util-Sentinel Pattern

读直到信号量模式:使用break语句在循环中结束最内层循环

1
2
3
4
5
while(true){
Prompt user and read in a value
if (value == sentinel) { break; }
Process the data value
}

for

以特定的循环次数重复执行某个操作

  • 基于条件的循环

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 一般模式
    for (init; test; step){
    statements
    }
    init;
    while(test){
    statements
    step;
    }
    // 二者等价

    // 常用模式
    for(int var=start; var <= finish; var++){
    // `var`:*index variable*
    statement
    }
    // 循环`finish - start`次
  • range-based for loop:基于范围的循环,C++11开始支持

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    for(type var: collection){
    statements
    }

    // `foreach.h`接口中的定义宏,提供类似功能
    foreach(type var in collection){
    }

    // C++编译器通过迭代器,将基于范围的循环转换为传统循环
    for(ctype::iterator it = collection.begin());
    it != collection.end(); it++){
    // `ctype`:集合类型
    }

编译、汇编、执行

步骤

Preprocess

预处理:生成.i预处理文件

  • 宏替换
  • 注释的消除
  • 寻找相关头文件/接口:除默认搜索路径,还可以通过环境变量
    设置
    • C_INCLUDE_PATH;C头文件搜索路径
    • CPLUS_INCLUDE_PATH:C++头文件搜索路径
    • CPATH:C/C++头文件搜索路径
1
2
$ g++ -E src.cpp > src.i
// 激活预处理,输出重定向到文件中

Compile

编译:将预处理后的文件编译为汇编语言,生成汇编文件.s

  • 编译单位为文件
1
$ g++ -S src.i -o src.s

Assemble

汇编:生成目标机器代码,二进制.o中间目标文件

  • .o通常仅解析了文件内部变量、函数,对于引用变量函数 还未解析,需要将其他目标文件引入
1
$ g++ -C src.s -o src.o
  • Windows下生成.obj文件

链接:链接目标代码,生成可执行程序

  • gcc通过调用ld进行链接
  • 主要是链接函数、全局变量
  • 链接器关注/链接二进制.o中间目标文件
  • Library File:若源文件太多,编译生成的中间目标文件 过多,把中间目标文件打包得到.lib/.a文件
1
$ g++ src.o -o a.out

执行

序列点

  • 对C/C++表达式,执行表达式有两个类型动作
    • 计算某个值
    • 产生副作用:访问volatile对象、原子同步、修改文件