我没有得到这个C / C ++的笑话
在阅读dailywtf.com上的这篇文章之后,我不确定是否真的有这个笑话。
它说那里有人更改了代码
int function() { int x; char data_string[15]; ... x = 2; strcpy(data_string,"data data data"); ... }
至
int function() { int x = 2; char data_string[15] = "data data data"; ... }
在代码中的任何地方,并由于某种原因确实膨胀的可执行文件的大小从1 CD到2(或者也许没有这样做?)。
很明显,我对C / C ++不熟悉这个笑话,但最奇怪的是,第二个代码清单似乎“更清晰” – 至less从我在学校讲过的内容(即初始化variables是好东西,不是坏的)。
根据编译器和编译器选项,像这样初始化
char data_string[15] = "data data data";
导致大量的移动指令将文字数据复制到堆栈。
调用strcpy
需要更less的指令。
在一个庞大的代码库上做这样的事情可以显着增加二进制大小。
当然,他没有花时间增加任何价值。
OIC,这是一个源代码stream失的问题
乍一看这两种forms是等价的。 第二个看起来更好,但他们做同样的事情。
但后来我看了引用的页面。
问题是这个新人搅动了源树,很多。 通过一个巨大的来源树造成一个毫无意义的变化是一种糟糕的forms。 当然,也许有一种风格比另一种风格稍微好一些,但是在实践中,将1000个三angular洲变成一个源代码控制系统,让人们永远渡过永恒是有道理的。
我怀疑这是一个源代码版本 ,或者其他一些未提及的复杂性导致编辑这些文件以扩大其分发。 对该网站的贡献是相当多的编辑,但基本上这个问题是可以理解的,没有具体的。
编辑一个文件以改变样式的问题之一是无意错误的机会增加。 当初级开发者这样做的时候,这个机会大大增加了。 即使对于有经验的人来说,也有墨菲定律要考虑的。 如果发生在发行之前,这真的是一个悬而未决的犯罪。
第二个代码确实是“更干净”,但是与文章大小有关的项目,认为这样的重构充其量是荒谬的,最坏的是容易出错。
但是,这种重构不会膨胀一个.Exe大小forms1到2个CD
我不能从中得到不同的行为。 我用LLVM试了一下:我不得不在返回值上添加一些cruft,这样LLVM不会优化任何东西,但是为wtf
和wtf2
生成的代码是完全相同的。 这个跆拳道是BAAAAAD
input
#include <stdio.h> #include <stdlib.h> #include <string.h> int wtf(int X) { int x; char data_string[15]; x = 2; strcpy(data_string,"data data data"); return 5*X+x+ data_string[X]; } int wtf2(int X) { int x = 2; char data_string[15]="data data data"; return 5*X+x+ data_string[X]; } int main(int argc, char **argv) { printf("%d\n", wtf(atoi(argv[1]))+wtf2(atoi(argv[1]))); }
输出:
; ModuleID = '/tmp/webcompile/_3856_0.bc' target datalayout = "ep:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32" target triple = "i386-pc-linux-gnu" @.str = internal constant [15 x i8] c"data data data\00" ; <[15 x i8]*> [#uses=3] @.str1 = internal constant [4 x i8] c"%d\0A\00" ; <[4 x i8]*> [#uses=1] define i32 @wtf(i32 %X) nounwind readnone { entry: %0 = mul i32 %X, 5 ; <i32> [#uses=1] %1 = getelementptr [15 x i8]* @.str, i32 0, i32 %X ; <i8*> [#uses=1] %2 = load i8* %1, align 1 ; <i8> [#uses=1] %3 = sext i8 %2 to i32 ; <i32> [#uses=1] %4 = add i32 %0, 2 ; <i32> [#uses=1] %5 = add i32 %4, %3 ; <i32> [#uses=1] ret i32 %5 } define i32 @wtf2(i32 %X) nounwind readnone { entry: %0 = mul i32 %X, 5 ; <i32> [#uses=1] %1 = getelementptr [15 x i8]* @.str, i32 0, i32 %X ; <i8*> [#uses=1] %2 = load i8* %1, align 1 ; <i8> [#uses=1] %3 = sext i8 %2 to i32 ; <i32> [#uses=1] %4 = add i32 %0, 2 ; <i32> [#uses=1] %5 = add i32 %4, %3 ; <i32> [#uses=1] ret i32 %5 } define i32 @main(i32 %argc, i8** nocapture %argv) nounwind { entry: %0 = getelementptr i8** %argv, i32 1 ; <i8**> [#uses=1] %1 = load i8** %0, align 4 ; <i8*> [#uses=1] %2 = tail call i32 @atoi(i8* %1) nounwind readonly ; <i32> [#uses=2] %3 = getelementptr [15 x i8]* @.str, i32 0, i32 %2 ; <i8*> [#uses=1] %4 = load i8* %3, align 1 ; <i8> [#uses=1] %5 = sext i8 %4 to i32 ; <i32> [#uses=1] %tmp2 = mul i32 %2, 10 ; <i32> [#uses=1] %6 = shl i32 %5, 1 ; <i32> [#uses=1] %7 = add i32 %6, 4 ; <i32> [#uses=1] %8 = add i32 %7, %tmp2 ; <i32> [#uses=1] %9 = tail call i32 (i8*, ...)* @printf(i8* noalias getelementptr ([4 x i8]* @.str1, i32 0, i32 0), i32 %8) nounwind ; <i32> [#uses=0] ret i32 undef } declare i32 @atoi(i8*) nounwind readonly declare i32 @printf(i8*, ...) nounwind
嗯,重新阅读文章:)
真正的跆拳道是当他应该修理内存泄漏时,他用这种变化触及整个解决scheme。
除此之外,做一个这样的改变也不会有什么大的影响,除了可能在其他的,也许更复杂的文件中破坏/引入错误。
是的,第二个代码是更清洁的,但取决于编译器,它可以导致发出更多的机器代码。 这完全是依赖于编译器的,但是WTF文章的要点是,在第二种情况下,编译器会像这样为每个代码片段分配一个string/整数值的副本,而在第一种情况下,每个程序只做一次。