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

int main() {
char __func_version__[] = “5″; // For source control
char 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 <stdio.h>

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 -v

Using built-in specs.

COLLECT_GCC=gcc

COLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc48/4.8.2/libexec/gcc/x86_64-apple-darwin13.0.0/4.8.2/lto-wrapper

Target: x86_64-apple-darwin13.0.0

Configured 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-multilib

Thread model: posix

gcc version 4.8.2 (GCC)

cs$gcc -std=c99 -Wall -O3 -g0 -o 5 5.c

apple@kissAir: cs$./5

0x7fff504fa920 0x7fff504fa930 0x7fff504fa910

2 + 2 = OP?


纳尼!

肿么__func_version__还比b要小,那么无论b加什么正数都无法指向前者了,当然有些人会说了,能够整数回绕啊。我呵呵了。那也不行哦,那样就不是“2+2=5”鸟,而是"2+xxxxxxxxxx=5"鸟了哦。尽管能够改变两个字符数组变量的位置来解决这一问题。即b[]定义放在__func_version__前面。但那也要"2+16=5"哦,我不知道gcc有没有什么编译选项能够pack堆栈变量滴。但我知道#pragma pack(1)是能够打包结构变量滴,so非常easy的我们能够加入例如以下代码:

#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变量布局的,请告知本猫,在此感谢。

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

#include <stdio.h>

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 -v

Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)

Target: x86_64-apple-darwin13.2.0

Thread model: posix

apple@kissAir: cs$clang -std=c99 -Wall -O3 -g0 -o 5 5.c

apple@kissAir: cs$./5

0x7fff57925936 0x7fff57925934 0x7fff57925933

2 + 2 = 5


所以说学C啥的光死看书不中啊,要学以致用啊,在此抛砖引玉。谢谢各位欣赏哦。

C语言里为何会有“2+2=5”的结果的更多相关文章

  1. c语言里用结构体和指针函数实现面向对象思想

    一.基础研究 观察如下两个程序a.c和b.c: A.c: B.c: 这两个程序都是要实现在屏幕上第10行40列打印一个绿色的字符c: 这两个程序的数据组织方式是一样的,都是使用结构体,而且对共性和个性 ...

  2. [原创]C语言里为何会有“2+2=5”的结果

    原文链接:C语言里为何会有“2+2=5”的结果 写这篇原创文章是因为看到了极客中的一篇文章<有趣各种编程语言实现2+2=5>,其中C语言是这样实现的: int main() { ″; // ...

  3. const分别在C和C++语言里的含义和实现机制

    const的含义        简单地说:const在c语言中表示只读的变量,而在c++语言中表示常量. C语言 const是constant的缩写,是恒定不变的意思,也翻译为常量,但是很多人都认为被 ...

  4. c语言里如何调用汇编里的变量?

    c语言里如何调用汇编里的变量? 汇编语言:是声明全局变量 .globl _end_ofs _end_ofs: .word _end - _start c语言:声明这个变量,然后再调用这个变量 void ...

  5. C语言里全局变量管理

    C语言里信息封装比較弱,仅仅有静态变量的文件作用域. 假设不加约束.非常easy造成全局变量满天飞. 假设定义一个全局结构体.把全局变量都放到这个GlobleVariate里,应该好管一些,至少比裸奔 ...

  6. [R]R语言里的异常处理与错误控制

    之前一直只是在写小程序脚本工具,几乎不会对异常和错误进行控制和处理. 随着脚本结构和逻辑更复杂,脚本输出结果的准确性验证困难,同时已发布脚本的维护也变得困难.所以也开始考虑引入异常处理和测试工具的事情 ...

  7. 理解Python语言里的异常(Exception)

    Exception is as a sort of structured "super go to".异常是一种结构化的"超级goto". 作为一个数十年如一日 ...

  8. 怎样在C语言里实现“面向对象编程”

    有人觉得面向对象是C++/Java这样的高级语言的专利,实际不是这样.面向对象作为一种设计方法.是不限制语言的.仅仅能说,用C++/Java这样的语法来实现面向对象会更easy.更自然一些. 在本节中 ...

  9. C语言里字符串的解析

      根据给定的字符串,按照一定规则解析字符串,卡住好几次,这次做个笔记,以供参考 函数名称:   strtok 函数原型:   char *strtok(char *s1, const char *s ...

随机推荐

  1. POJ - 1185 炮兵阵地 (状态压缩)

    题目大意:中文题目就不多说大意了 解题思路: 1.每行最多仅仅有十个位置,且不是山地就是平原,那么就能够用1表示山地,0表示平原,将每一行的状态进行压缩了 2.接着找出每行能放炮兵的状态.先不考虑其它 ...

  2. [课堂实践与项目]NavigationController与TabBarController的综合使用及易错点分析(包含消息提醒,app更新)

    陈述:我们在使用tabbarController的时候,我们总会和NavagationController联合起来.但是不联合的时候又是什么样的一种pool的情况呢?我们就单单的 TabBarCont ...

  3. 64位win7旗舰版搭建apache+php+mysql开发环境[转]

      我建议把apache.php.mysql都安装在一个文件夹中,比如:web/apache.web/php.web/mysql 1.安装apache2.2.25,请查看win7下安装VC9版本的ap ...

  4. hbase:应用开发

    开发环境: hadoop: hadoop-1.1.2 hbase: hbase-0.94.11-security eclipse:Juno Service Release 2 配置Eclipse 通过 ...

  5. Windows Azure 安全最佳实践 - 第 3 部分:确定安全框架

    构建云应用程序时,安全始终是计划和执行Windows Azure的首要核心因素.第 1 部分提出安全是一项共同责任,Windows Azure为您的应用程序提供超出内部部署应用程序需求的强大安全功能. ...

  6. DM6446开发攻略——u-boot-1.3.4移植(1)

    http://zjbintsystem.blog.51cto.com/964211/282387转载   UBOOT的版本更新速度比较快,截止今天,稳定正式的版本是u-boot-2009.11-rc2 ...

  7. poj 2126 Factoring a Polynomial 数学多项式分解

    题意: 给一个多项式,求它在实数域内的可分解性. 分析: 代数基本定理. 代码: //poj 2126 //sep9 #include <iostream> using namespace ...

  8. mac 修改系统配置参数 主机名 等

    mac 修改系统配置参数,可以使用 命令 scutil 参考网址: https://developer.apple.com/library/mac/documentation/Darwin/Refer ...

  9. Android开发okhttp,retrofit,android-async-http,volley?

    okhttp, retrofit,android-async-http,volley这四个框架适用的场合?优缺点?各位大大,请给一些建议.我准备开发一个新的APP 如果是标准的RESTful API, ...

  10. hadoop学习之ZooKeeper

    1. 什么是ZooKeeper? ZooKeeper是一组工具,用来配置和支持分布式调度. 它能处理分布式应用的“部分失败”问题. 什么是部分失败? 部分失败是分布式处理系统的固有特征,即发送者无法知 ...