为什么C语言结果总是21,C语言里为何会有“2 2=5”的结果

本文通过示例探讨了在C语言中如何利用字符串和整型变量相加的技巧,尝试使表达式2+2显示为5。通过调整代码和堆栈布局,最终在特定条件下实现了输出'2+2=5'的效果,强调了理解内存布局和编译器行为的重要性。
摘要由CSDN通过智能技术生成

2+2=5?

写这篇原创文章是因为看到了极客中的一篇文章《有趣各种编程语言实现2+2=5》,其中C语言是这样实现的:

int main() {char __func_version__[] = “5″; // For source controlchar b[]=”2″, a=2;printf(“%d + %s = %s\n”, a, b, a+b);return 0;}

有些童鞋可能会说,这不是偷换概念吗,拿字符串和int相加,是滴,但在这里请这些童鞋暂且幽默一回,想一想为何a+b会得出5的结果?你们实际动手编译了吗?结果是为5吗?

亲自动手

我动手编译了,结果不是5!!!

确切的说是一个不可打印的ascii字符,所以console显示的是:2+2= ,稍对C堆栈布局略有了解的都知道,其实这段代码最后试图打印的是__func_version__里的字符串'5',但遗憾的是不同编译器,甚至同一种编译器用不通编译选项生成得stack布局是截然不同的,这就无法保证精确定位b之后3字节正好指向__func_version__。

那么在gcc -O3下到底布局如何呢?我们略微修改一下代码:#include int main() {//char b[]='2', a=2;char __func_version__[] = '5'; // For source control char b[]='2', a=2;printf('%p %p %p\n',__func_version__,b,&a);/*for(int i=0;i<100;++i){printf('%d + %s = %s\n', i, b, i+b);}*/printf('%d + %s = %s\n', a, b, a+b);return 0;}

我们来看一下结果:

gcc -vUsing built-in specs.COLLECT_GCC=gccCOLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc48/4.8.2/libexec/gcc/x86_64-apple-darwin13.0.0/4.8.2/lto-wrapperTarget: x86_64-apple-darwin13.0.0Configured with: ../configure --build=x86_64-apple-darwin13.0.0 --prefix=/usr/local/Cellar/gcc48/4.8.2 --enable-languages=c,c++,objc,obj-c++ --program-suffix=-4.8 --with-gmp=/usr/local/opt/gmp4 --with-mpfr=/usr/local/opt/mpfr2 --with-mpc=/usr/local/opt/libmpc08 --with-cloog=/usr/local/opt/cloog018 --with-isl=/usr/local/opt/isl011 --with-system-zlib --enable-version-specific-runtime-libs --enable-libstdcxx-time=yes --enable-stage1-checking --enable-checking=release --enable-lto --disable-werror --enable-plugin --disable-nls --disable-multilibThread model: posixgcc version 4.8.2 (GCC) cs$gcc -std=c99 -Wall -O3 -g0 -o 5 5.capple@kissAir: cs$./50x7fff504fa920 0x7fff504fa930 0x7fff504fa9102 + 2 = OP?

还是不行?

纳尼!肿么__func_version__还比b要小,那么不管b加什么正数都无法指向前者了!

当然有些人会说了,可以整数回绕啊,我呵呵了。

那也不行哦,那样就不是“2+2=5”,而是'2+xxxxxxxxxx=5'了。

虽然可以改变两个字符数组变量的位置来解决这一问题,即b[]定义放在__func_version__前面,但那也要'2+16=5',我不知道gcc有没有什么编译选项可以pack堆栈变量滴,但我知道#pragma pack(1)是可以打包结构变量滴.

so,很简单的我们可以添加如下代码:#pragma pack(1) typedef struct __foo {char *b;char a;char *__func_version__;}foo; void print_5_by_struct(void){foo foo_v = {'2',(char)2,'5'};printf('%p %p\n',foo_v.__func_version__,foo_v.b);printf('%d + %s = %s\n',foo_v.a,foo_v.b,foo_v.a+foo_v.b);}

最终如愿以偿的打印了“2+2=5”,如果有其他童鞋知道gcc如何pack变量布局的,请告知本猫,在此感谢。

用clang试一下

有些童鞋又会说了,你这样结构太累赘鸟,太墨迹,不爽快!也好办,没说只能用gcc啊,我们试试clang吧 :)

#include int main() { char __func_version__[] = '5'; // For source control char b[]='2', a=2; printf('%p %p %p\n',__func_version__,b,&a); printf('%d + %s = %s\n', a, b, a+b); return 0;}

shell编译运行如下:clang -vApple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)Target: x86_64-apple-darwin13.2.0Thread model: posixapple@kissAir: cs$clang -std=c99 -Wall -O3 -g0 -o 5 5.capple@kissAir: cs$./50x7fff57925936 0x7fff57925934 0x7fff579259332 + 2 = 5

一句话总结

所以说学C啥的光死看书不中啊,要学以致用啊!!

在此抛砖引玉,谢谢观赏。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值