Chapter.116
告诉你的孩子,十八岁前,不会让他们因为钱而感到窘迫,这是为了塑造自信的性格,但十八岁后,会让他们因为钱而感到烦恼,这是为了培养独立的金钱观。 封面图片是由豆包 AI 生成的图片,一只望向星空的小狗。 转载自我自己的微信公众号,欢迎关注。
编译器视角下的未定义行为
前段时间,公司内有过一次关于未定义行为的讨论,我认为很有价值,也从中学到了东西,整理成文。 我简单介绍下几个相关的概念,并着重探讨一些未定义行为的程序用法。 什么是未定义行为未定义行为,Undefined Behavior(UB),并不是一个软件工程行业专有的话题,而是在各个领域都存在的普遍概念。 比如,开挖掘机可以挖沙子,也可以用来捞鱼,甚至,用它来挖鼻孔也可以,只要驾驶员有足够的能力驾驭它。而挖掘机说明书中,大概率不会提到用它捞鱼或挖鼻孔,虽然理论上有操作的可能性。 这些行为就可以称为使用挖掘机的未定义行为,又或者用手机捣蒜、用验尿机验茶水,都是 UB 行为。 未定义行为发生时,无法预测也无法保证它会产生什么后果,但一定是不推荐的行为。在软件上,通常指可以通过编译的编程用法,但不保证它实际会产生什么运行期效果。 程序存在未定义行为,就意味着它的执行结果有可能正确,也有可能错误,它可能会导致程序崩溃,但也可能看起来一切正常。而决定最终结果的因素,可能是编译器、操作系统、处理器或者运行时物理环境等。 不过需要明确的一点是,未定义行为应当被认为是一种 Bug。虽然很多系统编程中(尤...
Chapter.57
网络世界的信息失准
计算机体系结构模拟器简述
在芯片尚未流片,硬件还停留在前端设计时,芯片公司里那些软件开发工程师,已经在一个虚拟的环境上运行起了程序和算法。这个神奇的虚拟环境,就是计算机体系结构模拟器。 前几年,苹果公司在着手开发自研 M 系列处理器时,他们的软件工程师们,并不需要等到自家的芯片从工厂生产出来寄到公司,就能够让新的 MacOS 系统在新硬件上运行起来;而 Intel 的工程师们,他们在针对不同硬件上设计缓存算法时,并不需要机房里运行各种型号的处理器。 这就是计算机体系结构模拟器的魅力。它是一个桥梁,连接着软件设计和硬件设计,使得芯片公司可以让软件团队和硬件团队一起开始在新项目上发力,也可以让架构师们在构想的硬件模型上,验证和优化自己的设计和算法。 什么是计算机体系结构模拟器?简单来说,计算机体系结构模拟器(以下简称模拟器)是一套软件,这套软件用来重建一台计算机硬件。它可以精确或近似的模拟目标硬件的执行行为,让上层程序以为自己真的运行在一个硬件上。 每一项技术的诞生都有其存在的理由,模拟器的存在用来解决在硬件开发中的几个关键痛点。 时间和成本传统的硬件开发流程是先进行硬件设计,制造原型,原型测试,流片,...
Chapter.1071
理性看待中医和西医的关系
桃花源记
晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之。复前行,欲穷其林。 林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田美池桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。 见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。” 既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣精卒随其往,携刀循所志往。 至山口入,村人见甲士,皆持耒耜拒之。喝令降,不从。遂纵火焚屋,相攻竟日。男丁死者相藉,妇孺多掳,鲜血漫浸林麓,沾濡桃花。 事毕,召渔人,予金帛胁之缄口,令毁途志,对外云:“寻向所志,遂迷而不得路”。 南阳刘子骥,高尚士也,闻之,欣然规往。桃林尚在,然溪间浮尸残木,岸侧焦垣...
Effective Modern C++:微调
这一章有两个对更具体话题的讨论,虽然不适于普适的应用场景,但通过阅读学习,也能一定程度上加深对 C++ 编程语言的理解。作者发挥了他一贯的先假设再否定的行文风格,如果先验知识不足,很可能跟不上思路。 条款 41:对于可以复制的形参,在移动成本低并且一定会被复制的前提下,可以考虑按值传递条款标题很长,因为这个条款的前置条件很多,只有在这些前置条件都满足的情况下,才可以考虑将一个形参按值来传递。 阅读下文之前,需要已经明确搞清楚以下几个概念: 移动和复制 复制构造和复制赋值 左值和右值 左值引用和右值引用 和前边一样,我不会按原文的行文顺序去复述,而是按我理解地最容易被读者接受的顺序来介绍。 首先我们梳理下条款要说明的内容。我们知道,函数形参的传递方式,可以分为按引用(包括按指针,这里不讨论)和按值传递。为了保证参数传递的效率,通常会建议对于用户自定义类型或者复杂的容器类型使用按引用传递,避免拷贝数据带来的开销。 本条款想提出一个建议,如果一个类型作为形参时,无论它的复制成本有多重,当同时满足以下条件时,可以按值传递: 条件一:实现了复制操作(这意味着只移类型不满足) 条件二:...
Effective Modern C++:并发 API
C++11 中,提供了强大的并发特性,使得我们在编写并发程序时,不需要再去调用操作系统提供的并发接口(如 pthread 和 Windows 线程库)。Modern C++ 的程序员,必须熟练掌握并发 API 的用法,本章节会展开讨论几个使用 C++ 并发 API 时需要考虑和注意的问题。 条款 35:优先选用 std::async 替代 std::thread如果想要以异步运行的方式启动一个程序,有两种 API 可供使用: 1234567int doSomeAsyncWork(); // 直接启动一个线程 std::thread t(doSomeAsyncWork); // 启动一个任务 auto fut = std::async(doSomeAsyncWork); 两种方式各有不同的特点。书中介绍的什么是线程、什么是硬件线程和软件线程,这里不再重复赘述。 std::thread 就是启动一个软件线程,交给操作系统去调用执行。简单点来看,就是以一个独立的线程去执行这个异步函数。std::async 创建的对象 fut 的类型是 std::future,它的底层实现,也是一个软...
Effective Modern C++:lambda 表达式
lambda 表达式是 C++ 中一个极具颠覆性的语言特性,它主要用于创建匿名函数,从而可以更便捷地在一些场合下,快速生成函数对象后传递给需要的位置。这一章中,会陈述几个和 lambda 表达式相关的问题。 首先,需要明确几个概念。lambda 表达式是一种语法,它是静态的,编译器处理 lambda 表达式时,会利用它生成一个闭包类,然后在利用 lambda 表达式定义对象时,利用这个闭包类生成一个闭包对象。闭包对象是动态行为。 虽然我们无法方便地知道 lambda 表达式生成地闭包对象是什么类型,但好在可以用 auto 类型来让编译器自己推导。也由于闭包对象和其他对象没有什么大的不同,也可以实现赋值和移动等操作。 条款 31:避免使用默认捕获模式所谓默认捕获模式,就是直接使用 & 或 = 捕获当前作用域中所有可以捕获的对象(按引用或按值)。本条款建议,尽量将 lambda 表达式中需要使用到的外部对象,显式写在捕获列表中。虽然无法完全避免下文可能存在的问题,但默认捕获模式,可能会带来更危险的陷阱。 情况 1:通过引用捕获一个显而易见的问题是,lambda 表达式生成的闭...
Effective Modern C++:右值引用、移动语义和完美转发
在学习 C++ 时,当接触到右值引用和移动语义等概念时,就会劝退很多人。一方面,是因为这些概念本身已经不再是入门知识了,即使不懂,也可以写出一些程序了;另一方面,大多数教材中,并没有以最简洁易懂的方式完成这些知识的传授。 本章节会把这部分知识最底层最细节的内容,用另一个视角展示出来。但前提是,你需要已经理解什么是左值和右值,什么是引用这些基本的概念。 条款 23:理解移动语义和完美转发移动语义当我们写下一条赋值语句时,很多时候,实际上是完成了一次拷贝操作,也就是将数据复制一份,放入新声明的对象内存中。当数据量比较大时,并且赋值之后,原始的数据将不会再使用时,这种复制的代价会很大也很没必要。直觉的理解,就是直接把地址复用一下,就能避免复制一份的开销,这种行为看起来像把数据 “移动” 到新的对象内存中,从而提高了性能。 在 Modern C++ 中,我们使用 std::move 这个操作,来支持移动语义。 然而,C++ 烦人的地方就在于,很多你看似显而易见的概念,实际上却暗藏玄机。比如,std::move 并没有移动任何东西,后边提到的 std::forward 也并没有转发任何东西...














