分类 编程语言 下的文章

本来想写一大堆,后来发现这件事情在理论上是不可行的,然后就放弃了。

首先的原因是了解到作为gcc的C++标准库,libstdc++是后向兼容的(这里的“后”指时间上的“过去”),也就是说,新版本libstdc++支持旧版本编译出来的程序,而反过来不行。这是因为libstdc++库是以一种类似于增量更新的方式实现的,使用strings指令查看libstdc++.so文件可以发现,它包含了从最早版本以来一系列的符号支持,这也正是其后向兼容的原因。

由于没有前向兼容,导致过去的gcc不是适应未来的编译环境。以我的体验为例,用gcc 8.4.1编译gcc 4.9.4,前者的libstdc++ABI应用二进制接口版本是CXXABI_1.3.9,而后者的只有CXXABI_1.3.8

而问题正是出在这里。由于gcc和libstdc++属于紧密耦合的关系,gcc会使用自带的libstdc++而不是当前系统的版本来提供运行环境。使用了gcc 8.4.1编译的gcc 4.9.4自然是按照前者的运行库,使用了CXXABI_1.3.9;而当gcc 4.9.4运行的时候,从自己的libstdc++库里一翻,只支持到CXXABI_1.3.8,啪的一下就因为找不到符号报错了,很快啊。

当然这个可以通过使用LD_PRELOAD强行让gcc 4.9.4使用系统libstdc++库来解决这个问题,但是后面还有更麻烦的。我前几年编译gcc的文档里提到过,gcc的编译是自举的,相当于先编译gcc核心-编译c++库-再编译gcc扩展这个流程。

然而,使用LD_PRELOAD只是解决了第二步中的一个问题,重点在于libstdc++在版本更新的时候,可能不止更新ABI,还更新了上层的API,可能编着编着发现函数不见了,或者签名改了,直接报错中断,问题还没等到链接阶段就已经出现了。要解决这个问题约等于更改gcc对应的libstdc++库依赖,这严重违反了两者现有的紧密依赖的特点。这也正是我选择放弃降级编译gcc最主要的原因。

最后的结论是,CentOS 8是个什么垃圾东西,CentOS 7+devtoolset它不香吗

在这些时候,我可以附和着笑,leader是决不责备的。而且leader见了孔乙己,也每每这样问他,引人发笑。孔乙己自己知道不能和他们谈天,便只好向孩子说话。有一回对我说道,“你写过代码么?”我略略点一点头。他说,“写过代码……我便考一考。C++的变量初始化,怎样写的?”我想,讨饭一样的人,也配考我么?便回过脸去,不再理会。孔乙己等了许久,很恳切的说道,“不能写罢?我教给你,记着!这些写法应该记着。将来做leader的时候,算KPI要用。”我暗想我和leader的等级还很远呢,而且leader也从不把C++语法算进KPI;又好笑,又不耐烦,懒懒的答他道,“谁要你教,不是一个等号的事么?”孔乙己显出极高兴的样子,将两个指头的长指甲敲着柜台,点头说,“对呀对呀!变量初始化有四样写法,你知道么?”我愈不耐烦了,努着嘴走远。孔乙己刚用指蘸了酒,想在柜上写代码,见我毫不热心,便又叹了口气,显出极惋惜的样子。

int val(4);
int val {4};    // *
int val = 4;
int val = {4};  // *

* 使用大括号{}进行变量初始化的功能来自于C++11标准,对于赋值过程有严格的检查,比如不允许使用类型缩窄(例如,把int类型值赋值给char类型变量,即使程序员知道这个int值可以使用char类型正确保存)。

呜呜呜,C语言白学了.jpg

今天看C4P(C++ Primer Plus)才发现两个东西:

  1. define不是全文替换,而是单词替换;

假设有如下代码段:

#include ...
#define test "sss"
...
    char s[] = test;
    float tested = 1;
    printf("%s test\n", s);
...

输出是sss test而不是sss "sss"。(C++支持连续字符串自动拼接) (如果要验证define会不会替换变量名的话,得要至少两个文件才行)

  1. C语言的字符常量是按照int大小(通常是4Bytes)存储的。C++早期也是,后来才改成了和char一致的大小(按int存储的话会导致cout以数字形式输出字符常量的ASCII码)。

import asyncio, time

async def a(s):
    import time
    await asyncio.sleep(1)
    print(s)

async def b():
    asyncio.ensure_future(a('asd'))
    asyncio.ensure_future(a('qwe'))
    await asyncio.sleep(2)
    print('b')

loop = asyncio.get_event_loop()
B = b()
loop.run_until_complete(B)

要注意的有几个地方:

  1. async def的函数在哪里都可以调用,但是在调用的时候,必须显式等待其结束,否则会出现RuntimeWarning: coroutine 'xxx' was never awaited。在async函数中调用async函数的话,可以使用await关键字直接阻塞等待;在非async函数中则必须设置事件循环器event looper,然后通过事件循环器对所有的异步async函数进行阻塞式等待,避免主线程在子线程完成前退出;
  2. 在协程中休眠线程不能用time.sleep(),而应该用asyncio.sleep(),否则休眠的是整个进程而不是目标协程;
  3. asyncio.ensure_future()比较有意思,提供了类似“用完就扔”的协程运行模式,不必在后续显式await该协程。