欢迎光临散文网 会员登陆 & 注册

生动形象解释strict-aliasing规则的寓言故事一则

2023-06-29 22:29 作者:双笙子佯谬  | 我要投稿

有一天,一个叫做指针的小女孩去参观了一个名为“洗佳佳”的神奇工厂,那里生产各种各样的数据。指针很好奇,他想看看工厂里都有什么。进门时一个自称“iso管委会”的工作人员拦住了他,说: 工厂里的数据是有类型之分的,你如果需要访问工厂里某个类型的数据,那你就得拿着那个类型的通行证。指针说: 我觉得int类型最常用,我想要申请一个int通行证。工作人员打了个电话给管委会的领导,批准了,于是工作人员拿出一张白卡,拿出int图章印了上去,说: 这就是你的通行证了,你只能用它访问int类型的数据,如果你需要访问其他类型的数据,请再次和我们联系,我们会帮你再做一份不同类型的通行证的。他拿着通行证,上面写着“int”,表示他有访问int类型的数据的权限。 进入工厂后,指针在整型车间找到了一个叫做int的数据,它是一个4字节的整数。指针用他的int通行证访问了这个int数据,没有问题。我申请的就是int通行证,int通行证访问int类型是完全没问题的,他心想。 他查阅员工手册,发现int通行证不仅可以访问int数据,也可以访问其他类型的数据,前提是他必须遵守一个规则:strict-aliasing。 strict-aliasing的规则是这样的:指针只能访问和他通行证上相同类型的数据,或者是一个该类型的超集,比如一个包含该类型的结构体(struct/class)或联合体(union)。如果指针想要访问一个更小的类型,或者是一个不同的类型,他必须重新领取一张通行证,而不是对通行证进行涂改,否则他会被抓住并赶出工厂。例如int通行证不能用于访问short,但是可以访问unsigned int。另外,char和unsigned char是两个特例,用任何类型的通行证都可以访问。 指针找到了一个叫做short的数据,它是一个2字节的整数,指针想用他的int通行证访问这个short数据,但被工作人员拒绝了。他觉得这个规则很奇怪,他想知道为什么管委会要这样限制他。他接下来决定去试试看能不能破坏这个规则,看看会发生什么。 然后,他发现了一个门牌上写了char[4]的房间,打开一看,里面有4个摇篮,分别睡着4个char小孩,呆呆地看着他。指针想要访问这个数据,但是他没有换通行证,因为他觉得这样太麻烦了。他觉得反正我的通行证int是4字节的,用于访问同样是4字节的char[4]应该没问题吧?他拿起通行证来观察,发现通行证正面虽然写着“int”,背面却淡淡写着“char”,他觉得蹊跷,查了员工手册才知道,原来洗屁屁工厂的每个通行证背面都印有char,意味着每一张通行证都可以当成char通行证使用。于是他把通行证翻了个面,当成char通行证用,访问了这4个char数据,没有被发现。之所以开了这个后门或许是为了方便有时候需要把数据作为字节处理,例如把网络收发包得到的char缓冲区当成任意符合类型去处理,他心想。 指针很高兴,他觉得自己很聪明。他继续寻找其他类型的数据,试图用他的通行证访问它们。他找到了一个叫做unsigned int的数据,它是一个四字节的无符号整数。指针想要访问这个数据,但是他没有换通行证,因为他觉得这样太麻烦了。他觉得反正我的通行证int是4字节的,用于访问同样是4字节的unsigned int应该没问题吧?他偷偷地拿出水笔,在通行证上的“int”字样前潦草地写上“unsigned”前缀,改成了“unsigned int”,访问了这个unsigned int数据,没有被发现。 他后来翻看员工手册,才知道,原来每次派发整数类型的通行证时,都会附赠一张贴纸,贴纸上写着unsigned,把unsigned贴纸贴在通行证上作为前缀,即可访问整数的无符号版本的数据。而他刚才随手把那贴纸扔了,自己写上unsigned也达到了同样的效果。之所以开了这个后门或许是为了无符号和有符号类型转换的方便起见,他心想。 他来到精加工车间,发现里面都是一些结构体类型,他看到一个结构体struct C { int i; short s; };他想要顶撞一下规则,于是拿着int通行证去访问这个结构体的数据,他本以为会像刚才那样遭到阻拦,却不料成功访问了,没有被工作人员阻止。原来根据规则,只要结构体或联合体的成员中含有一个通行证上的类型,也是可以访问的。这应该是出于有时候需要取成员变量指针的方便,他心想。 指针更加高兴了,他觉得自己很厉害。他继续寻找其他类型的数据,试图用他的通行证访问它们。他找到了一个叫做float的数据,它是一个4字节的单精度浮点数。指针想要访问这个数据,但是他没有换通行证,因为他觉得这样太麻烦了。他觉得反正我的通行证int是4字节的,用于访问同样是4字节的float应该没问题吧?他从背包里掏出一款“reinterpret_cast牌”的修正带,偷偷地把他的通行证上的“unsigned int”字样涂掉,改成了“float”。他拿着篡改的通行证访问了这个float数据,他违规的行为没有被发现。 指针非常高兴了,他觉得自己无所不能。但是就在这时候,一声巨响打破了工厂的平静。原来,指针在访问float数据时,不小心改变了它的值。这个float数据正好是工厂里最重要的数据之一,它控制着工厂的运转。由于指针改变了它的值,而工厂管委会觉得自己只发过int的通行证,认为不会有人修改过这个float值,因为float值只有颁发了float通行证才能修改。并不知情的工程师想当然地拿着错误的float数据进行运算,导致工厂出现了严重的故障,并且开始爆炸。 指针吓坏了,他想要逃跑,但是已经来不及了。工厂里所有的安保人员都发现了指针,并且追捕他。指针被抓住了,并且被严厉地批评教育的一番。

经历了这一次的教训,他认识到了随意用reinterpret_cast涂改通行证的危险。一位名叫“本贾尼·斯特劳斯特卢普”的安全人员告诉他,如果想要安全低在float和int之间按位转换,可以用以下这些方法: 1. 联系一名叫做std::launder的清洁人员,他会帮你重新打印通行证,由于他会利用对讲机通知管理人员进行调度,所以他帮你涂改通行证是安全的。 2. 当你所在的工厂老板是“GNUC”时,可以走进办公室对他说一句-fno-strict-aliasing,开明的他就会帮你废除这个规则,strict-aliasing规则废除后,通行证就可以随意涂改,但由于每对通行证之间都可能指向同一个对象,管理人员不得不假定每一个指针都是aliasing的情况,所以也会妨碍到管理人员对工程的排班和优化,对工厂流程效率产生负面影响。 3. 还可以用一台叫做std::bit_cast的“重塑机”进行转换,不过他和,std::bit_cast需要把float对象塞到机器里,并吐出int对象,而不是插入指向float的通行证,他内部是通过把源类型用memcpy逐字节拷贝到unsigned char暂存,然后重新reinterpret_cast成目标类型读取的,由于。 4. 可以用一个联合体union { int i; float f; }进行转换,抽象来说是由于规则说通行证可以访问联合体的成员,因此可以借助联合体作为桥梁来进行转换,具体来说是因为联合体创建时,管理人员就知道这两个类型之间可能产生按位转换,从而对结构体内的所有成员留个心眼,不会产生调度事故。 经过一顿安全教育后,指针害怕极了,他再也不敢乱动不兼容类型的数据。有一次指针需要修过一个float值,有了上次的教训,他不敢直接拿着int通行证修改float的值。于是他找来一位名叫std::launder的专业清洁工,只见清洁工拿起int通行证,在上面进行了一波“洗刷刷洗刷刷”,于是就把通行证上int的水印去掉了。然后清洁工打了个电话给管委会,申请报备了float的访问权限,经过管委会签字同意后,才往“洗刷刷”刷干净的通行证印上“float”的字样。管委会说,给,这是你船新的float通行证,用这张官方报备过的通行证,就可以安全访问float值了。指针用float通行证修改了float,这时另一个指针也想修改同一个float值,他也申请了一份float通行证。管委会发现当前批出去了两份float通行证,于是派出安全专家维护float车间的秩序,让指针和另一个指针有序访问float值,工厂没有发生故障,今天又是和平的一天。

生动形象解释strict-aliasing规则的寓言故事一则的评论 (共 条)

分享到微博请遵守国家法律