3.17
现在可以嘲笑一下自己昨天定下的生活小目标了。十点起床没吃早饭,下午从 13:30 睡到 18:30,好在午饭和晚饭的时间还是十分规律的。不过晚上一直在刷 bilibili,稍稍入门一下了德扑。
上午写了 ADS(Advanced Data Structure)的 HW0,内容实在很若指。不过鼓捣的时候还是学到了一点东西。
研究了一下 C++ 程序有符号整数的乘法溢出行为。在 g++ (Ubuntu 11.4.0-1ubuntu1~22.04.2)
11.4.0 的默认优化强度 -O0 默认 C++17 标准以及 x86_64-linux-gnu 环境下的行为是直接翻译成双参数的 imul 指令,所以结果就是最终完整结果的 64 位表示的低 32 位直接截断。
还检索了一下一份 C++ 源代码加上哪些参数可以唯一确定生成的机器码,不过并没有仔细研究,初步的结果是(按照感觉上的由软到硬排序)
- C++ 标准版本、外部库的实现(通过版本确定)、(部分)标准库实现(通过版本确定)
- 预处理器实现、定义的预处理宏
- 编译器实现(通过版本确定)、编译参数(包括优化级别、各种优化细节、各种处理细节、调试信息等)
- 链接器实现(通过版本确定)、链接行为的其他因素
- 目标平台:架构(如 x86_64、armv8、riscv64)、供应商、操作系统、应用程序二进制接口
然后想要给 C++ 中的整数类型提供一个完整的不涉及内存模型仅通过描述其一些表观行为的刻画(实际上标准本身做了这件事),然后失败了,原因是一些表观行为不得不牵扯到内存模型的层面上。然后开始和 gemini 老师进行高强度对话,发现其提供的形式性、严谨性总是不尽人意。由于有些东西 cppreference 上也没有提到,所以最后诉诸标准提案的草稿。
奇怪的东西
C++ 中的所有整数类型(integral types)按照作用可以被分类为 standard integer types,boolean types,character types。
其中 standard integer types 描述的作用是算术。所有的 standard integer types 可以被如下形式刻画:[<sign_modifier>] [<size_modifier>] [<type>]。
<type>取值char、int。当且仅当任意<size_modifier>或<sign_modifier>被提供时,<type>才是可选的,此时等价于取值int。<sign_modifier>取值signed、unsigned。当被忽略时,等价于取值signed。<size_modifier>在<type>为int或者被省略时,取值short、long、long long;在<type>为char,<size_modifier>没有有效取值。<type>、<size_modifier>、<sign_modifier>三者的位置可以任意互换。注意此处标准规定了一个非常反直觉的特殊情况:如果<size_modifier>是long long,那么这两个 long 是可以拆开然后进行位置交换的,即unsigned long int long和unsigned long long int是等价的。如果以后标准要支持 128 位整数类型难道要写成long long long int?
最终按照语义等价关系商下来剩 10 个等价类,分别是 char、short int、int、long int、long long int 以及它们对应的 unsigned 版本。值得注意的是,标准并没有规定每种类型的宽度确定值,而是规定最短位数:
charat least 8shortat least 16intat least 16longat least 32long longat least 64
作为补偿,标准规定了一些数据模型(data models)并且规定了这些数据模型下这 10 种类型的宽度确定值