新编《守株待兔》—C语言版—兼聊为什么不应该用%d格式转换输出指针

      有时候我感到和某些人讲道理比教猫唱歌还费劲。
      比如说,当你指出某些书上讲“鸡下鸭蛋”是胡说八道时,立刻就会有人跳出来说:鸡下不下鸭蛋并不重要,国内n多人都是看那本讲“鸡下鸭蛋”的书启蒙的,你掌握“下蛋”这一个知识点就够了,记住思想就好了。(参见http://www.cnblogs.com/pmer/archive/2011/08/08/2131075.html 评论部分)
      讨论和交流的前提是双方有共同的逻辑基础,如果您觉得“鸡下鸭蛋”并不荒谬而有思想的话,建议就不要再浪费时间继续看下去了,此文仅供那些懂得鸡不可能下鸭蛋的人参考。

      韩非老师讲过一个故事:
      宋人有耕田者。田中有株,兔走触株,折颈而死。因释其耒而守株,冀复得兔。兔不可复得,而身为宋国笑。
      每个人都会笑这个人很傻,包括幼儿园的小朋友都会。然而,假如这个人第二天又得一兔呢?还有多少人觉得他很傻?
      再假如这个人第三天依旧得一兔,有多少人依然坚信这不过是一个偶然事件?有多少人会跟着宋人一起去捡兔子呢?
      我知道一定会有人会跟着宋人去捡兔子。
      但假如此时有一个人,所有的人都知道他从不说假话,这个人说,那颗树下,从前并不是天天有兔子,以后也未必就天天会捡到兔子。尽管人人都知道这个人从不说假话并且永远不说假话,但是面对兔子的诱惑,这时还会有多少人还会相信他呢?
      这时这个人又说,稍微远一点的那颗树下一定会天天有兔子,而在近处那颗树下,尽管现在捡到了几只兔子,可能某天会遇到有害的虫子。
假设人人都知道这个人说的一定是真话,相信这时会有人肯每天走得稍微远一点,以得到必然会获得的兔子并躲避有害的虫子。但是会不会有人依旧坚持到近处那颗树下去捡兔子呢?
      如果有人依旧坚持到近处那颗树下去捡兔子,这个人是否和韩非讲过的那个宋人一样可笑呢?
      结论留给大家自己去做,下面谈C语言里面的兔子。

printf("%o",p);                                
作用是以八进制形式输出指针变量p的值。
————谭浩强 ,《C程序设计》(第四版),清华大学出版社,2010年6月,p224

      这就是在近处那颗树下捡到的第一只兔子。因为在C标准和K&R的《C程序设计语言》(第二版)中,只说过%o用于转换输出int类型的值,从没说过%o可以用于转换输出指针。
      然而这确实是一只兔子,因为:编译通过了,运行也没捅漏子,居然有一个貌似正确的结果。
      于是开始在近处那颗树下捡第二只、第三只兔子……:

#include <stdio.h>
int main()
{
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
printf(
"%d,%d\n",a,*a);
printf(
"%d,%d\n",a[0],*(a+0));
printf(
"%d,%d\n",&a[0],&a[0][0]);
printf(
"%d,%d\n",a[1],a+1);
printf(
"%d,%d\n",&a[1][0],*(a+1)+0);
printf(
"%d,%d\n",a[2],*(a+2));
printf(
"%d,%d\n",&a[2],a+2);
printf(
"%d,%d\n",a[1][0],*(*(a+1)+0));
printf(
"%d,%d\n",*a[2],*(*(a+2)+0));
return0;
}

————谭浩强 ,《C程序设计》(第四版),清华大学出版社,2010年6月,p248~249
      有了第一次成功的经验,这次的胆子更大了,居然用%d格式转换输出指针。好在这回没有窜出一条有毒的虫子(BUG),跑出来的还是一只兔子。这可能让在旁边捏着一把汗观看的人感到非常意外,侥幸捡到兔子简直是交上了狗屎运。要知道,%d可是有可能输出一个负的十进制整数的啊。
      虽然C标准及K&R只说过%d用于转换输出int类型的值,从没说过%d可以转换输出指针。然而这次还是捡到了兔子。

      然而历史有没有告诉过我们用%d或%o的方法捡不到兔子呢?有。但我不想和大家一道重温Intel8086的16位段地址和16位偏移地址组合成20位地址那噩梦一般的历史,只在这里陈述一下简单的历史事实:历史上有些C语言编译器中的各种不同类型的指针的大小并不相同,这时你绝对不可能把指针看得和int等同,更何况即使指针长度都和int长度相同也没有理由把指针看成int——它们毕竟不是相同的数据类型。
      这种噩梦般的历史一去不返了吗?NO!没有任何理由可以让我们作出这样的判断。

      那么,C标准有没有说过在哪颗树下一定可以捡到兔子呢?有!C标准和K&R都说过,指针应该用%p格式转换输出。
      那么为什么用%d或%o(也有人用%u)可以捡到兔子呢?相信一定会有心怀不甘或恼羞成怒的人这样反问。
      答案是,C标准没有说用%d或%o转换输出指针会产生什么样的结果(包括编译报错和警告),因此这是一个未定义行为(undefined behavior)。所谓未定义行为往往比你想象的还未定义,它可能在某些情况下假情假意地迎合你,但是假意迎合不表示遵从,它保留随时和你翻脸的权利。所以未定义行为是一种比立即发作的BUG更危险的BUG,不了解这一点就等于并不懂得C。
      现在是否还有宋人不愿意走得稍微远一点,不用%p转换输出指针而用%d、%o或%u转换输出指针呢?相信还会有。有的宋人是不到黄河心不死、不碰南墙不回头的,非得碰得头破血流不可。这也是没办法的事。我这里只能友情提醒一下,南墙不远:据网友ChiyuT报告,在他的机器上,指针是8个字节,不过int还是4个字节(http://bbs.chinaunix.net/thread-3582216-7-1.html)。

原文地址:https://www.cnblogs.com/pmer/p/2149879.html