linux中命令cpp是C Preprocessor的缩写,而不是C++那个后缀名称。
先来个关于printf奇怪用法的程序,这个程序是一个牛人写的,得过大奖,背景在此就不多说了,有闲情想了解的同志们去百度吧。在linux或unix平台上运行的结果是unix。
这个程序在CU上有帖子讨论:
http://bbs.chinaunix.net/thread-580042-1-1.html
在Linux forum上也有讨论:
下面是后者中的一些解答部分,本人才疏学浅,不敢在此发表自己见解。
因为:数组str[n] <==>(n)[str]
所以:(unix)["have"] + "fun" - 0x60 <==> "have"[1]+"fun"-0x60
即: 'a'+"fun"-0x60
因为:'a' = 0x61
所以:变为“fun”+1(隐含了同类型运算优先?)
因为:“fun”是个char型指针
所以:指向“un”
因为:/021是一个八进制数,代表第一个字符
所以:&1["/021unix/012/0"]<==>"unix/012/0"
因为:012是八进制的换行,/0是字符串结束
所以:最终结果是
unix
[提示符]$
printf(&unix["/021%six/012/0"], (unix)["have"] + "fun" - 0x60);
==> printf(&1["/021%six/n/0"], (1)["have"] + "fun" - 0x60);
==> printf(&"/021%six/n/0"[1], "have"[1] + "fun" - 0x60);
==> printf("%six/n", 'a' + "fun" - 0x60);
==> printf("%six/n", 0x61 + "fun" - 0x60);
==> printf("%six/n", "fun" + 1);
==> printf("%six/n", "un");
理解这个程序需要知道数组的那个诡异但正确的用法,取地址,指针运算,UNIX/Linux编译器的熟悉程度(说白了就是要知道unix是一个已经预定义好的宏,见下文),当然还要知道十六进制和八进制表示方法。
$cpp -dM /dev/null > macro-on-linux.txt
(其中的/dev/null可用C文件代替,添加选项-std=c99可得不同结果,详细用法请man cpp,实际结果请实践之)
macro-on-linux.txt文件中内容如下,这里删除了其中很多内容,其中unix藏在某个角落里,所以,上面的程序如果使用下面许多宏来代替,结果是一样的。
#define __DBL_MIN_EXP__ (-1021)
#define __FLT_MIN__ 1.17549435e-38F
#define __unix__ 1
#define __DBL_MIN_10_EXP__ (-307)
#define __FINITE_MATH_ONLY__ 0
#define __GNUC_PATCHLEVEL__ 0
#define __linux 1
#define __DEC32_EPSILON__ 1E-6DF
#define __unix 1
#define __LDBL_MAX_EXP__ 16384
#define __linux__ 1
#define __SIZEOF_LONG__ 4
#define __DECIMAL_DIG__ 21
#define __gnu_linux__ 1
#define __LDBL_HAS_QUIET_NAN__ 1
#define __GNUC__ 4
#define __DEC32_MIN__ 1E-95DF
#define __DBL_MAX_EXP__ 1024
#define __SIZEOF_SIZE_T__ 4
#define __NO_INLINE__ 1
#define __i386 1
#define __FLT_MANT_DIG__ 24
#define __VERSION__ "4.3.0 20080428 (Red Hat 4.3.0-8)"
#define unix 1
#define __i386__ 1
#define __SIZE_TYPE__ unsigned int
#define __ELF__ 1
#define linux 1
#define __FLT_MIN_10_EXP__ (-37)
#define __INTMAX_TYPE__ long long int
#define i386 1
#define __STDC__ 1
#define __PTRDIFF_TYPE__ int
#define __GNUC_GNU_INLINE__ 1
这些宏都是预定义好了的,就像__cpluscplus,__func__那些一样。网上说是编译器内建的,在具体文件是找不到的,我功底不深,不能从原理上解释,见谅。
个人觉得,如果想在linux下搞些开发,这些知识应该要懂一点,多知道一些总不是坏事。