﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-C/C++图形图像的世界</title><link>http://www.cppblog.com/misakamm/</link><description>图形与游戏编程</description><language>zh-cn</language><lastBuildDate>Thu, 23 Apr 2026 10:14:13 GMT</lastBuildDate><pubDate>Thu, 23 Apr 2026 10:14:13 GMT</pubDate><ttl>60</ttl><item><title>C/C++宏的奇技淫巧</title><link>http://www.cppblog.com/misakamm/archive/2012/01/16/164258.html</link><dc:creator>御坂美琴</dc:creator><author>御坂美琴</author><pubDate>Mon, 16 Jan 2012 08:22:00 GMT</pubDate><guid>http://www.cppblog.com/misakamm/archive/2012/01/16/164258.html</guid><wfw:comment>http://www.cppblog.com/misakamm/comments/164258.html</wfw:comment><comments>http://www.cppblog.com/misakamm/archive/2012/01/16/164258.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/misakamm/comments/commentRss/164258.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/misakamm/services/trackbacks/164258.html</trackback:ping><description><![CDATA[来源：<a href="http://blog.misakamm.org/p/209">http://blog.misakamm.org/p/209</a><br />
<font face="新宋体">宏的主要作用就是简化代码编写，简化一些需要重复编码的地方，以得到看起来更优雅的代码。但宏要用得好并不容易，用的不好很容易引发灾难性的后果。本文会介绍宏比较偏门但又很实用的技巧。<br />
首先就是最常用的技巧（<a href="http://blog.misakamm.org/p/209">http://blog.misakamm.org/p/209</a>）：<br />
#define MACROCAT( x, y ) MACROCAT1 ( x, y )<br />
#define MACROCAT1( x, y ) x##y<br />
#define TOSTRING( s ) #s<br />
MACROCAT把x和y展开后连結，而TOSTRING把s转化为字符串，比如可以printf(TOSTRING(%s), TOSTRING(abcdefg));<br />
然后，因为宏不能递归，但可以做递归模拟，我们可以这样玩。比如要生成n位的二进制数并且从小到大构成的字符串（用到前面的宏）：<br />
<pre><font color=#FF0000>#define </font><font color=#800080>BIN_0</font>(arg) <font color=#800080>TOSTRING </font>( arg )
<font color=#FF0000>#define </font><font color=#800080>BIN_1</font>(arg) <font color=#800080>BIN_0</font>(<font color=#800080>MACROCAT</font>(arg, 0)) <font color=#FF00FF>"," </font><font color=#800080>BIN_0</font>(<font color=#800080>MACROCAT</font>(arg, 1))
<font color=#FF0000>#define </font><font color=#800080>BIN_2</font>(arg) <font color=#800080>BIN_1</font>(<font color=#800080>MACROCAT</font>(arg, 0)) <font color=#FF00FF>"," </font><font color=#800080>BIN_1</font>(<font color=#800080>MACROCAT</font>(arg, 1))
<font color=#FF0000>#define </font><font color=#800080>BIN_3</font>(arg) <font color=#800080>BIN_2</font>(<font color=#800080>MACROCAT</font>(arg, 0)) <font color=#FF00FF>"," </font><font color=#800080>BIN_2</font>(<font color=#800080>MACROCAT</font>(arg, 1))
<font color=#FF0000>#define </font><font color=#800080>BIN_4</font>(arg) <font color=#800080>BIN_3</font>(<font color=#800080>MACROCAT</font>(arg, 0)) <font color=#FF00FF>"," </font><font color=#800080>BIN_3</font>(<font color=#800080>MACROCAT</font>(arg, 1))
<font color=#0000FF>int </font><font color=#FF0000>main</font>()
{
<font color=#FF0000>puts</font>(<font color=#800080>BIN_4</font>());
<font color=#0000FF>return </font>0;
}</pre>
<br />
这里要注意的是，比如BIN_2()，实际上展开的结果是<br />
<pre>"0" "0" "," "0" "1" "," "1" "0" "," "1" "1"</pre>
不过c/c++规定这样连写的字符串，编译时就会合并成一个，于是就能用puts直接完整输出结果了<br />
如果你想得到更多的位，很简单，只要你不介意，上面的宏复制并改改数字就可以了<br />
不过，这样一改要改若干个数字，比较麻烦，能不能让它工作得更好？比如只要改宏名？<br />
这个时候，就要用更富有技巧性的一招了：让每个宏多一个参数n，然后前面的BIN_x使用MACROCAT把它与数字连结起来，不就可以了么？<br />
想法不错，不过问题是宏本身没有做减法的能力，能做的仅仅是替换。减1应该怎么实现呢？<br />
其实不难，见以下定义：<br />
#define DECVAL_1 0<br />
#define DECVAL_2 1<br />
#define DECVAL_3 2<br />
#define DECVAL_4 3<br />
#define DECVAL_5 4<br />
#define DECVAL_6 5<br />
#define DECVAL_7 6<br />
#define DECVAL_8 7<br />
#define DECVAL_9 8<br />
#define DECVAL( n ) DECVAL_##n<br />
好了，有了这个利器，我们就可以对原宏改造了，先拿0号和1号宏开刀：<br />
<pre><font color=#FF0000>#define </font><font color=#800080>BIN_0</font>(n, arg) <font color=#800080>TOSTRING </font>( arg )
<font color=#FF0000>#define </font><font color=#800080>BIN_1</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BIN_</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 0)) <font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BIN_</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1))</pre>
看得懂替换了一些什么吗？这样，后面的2,3,4,5号，只要复制一下1号的定义，改一改宏名就解决问题了<br />
<font color=#0000FF>思考题：<br />
这里生成的二进制结果是带前导0的，如何改写能使生成的结果不带前导0？<br />
source: <a href="http://blog.misakamm.org/p/209">http://blog.misakamm.org/p/209</a><br />
</font><br />
使用此法可以&#8220;递归&#8221;式生成很多类似代码，同时这个技巧也非常的实用，但递归构造并不容易，需要编写的人仔细想清楚，否则很容易出错，特别要注意宏展开的时机，一般不直接使用MACROCAT1宏，因为那个很可能不是你想要的结果<br />
之后，到C99标准出台后（也就是说，下文内容与bc3/tc/vc6不兼容），宏里面多了一个狠角色：可变参数个数宏<br />
比如可以 #define PRINTF(...) fprintf(stdout, __VA_ARGS__)<br />
其中__VA_ARGS__代表了&#8216;...&#8217;部分的全部参数，这样可以轻松的重定义库函数里不定参数的函数的输出行为，比如printf重定向到文件（虽然也可以用freopen实现，但只想说明宏也可以这样搞）<br />
好了，下文将区分编译器来介绍，一共分为两派，vc派和gcc派（包括clang/objc），因为两者对以下代码的处理并不一致，需要使用略为不同的宏来实现，目前我也只遇到这两派。<br />
现在的目的是这样，因为__VA_ARGS__包含了若干参数，我怎么才能知道里面参数有多少个呢？<br />
比如写一个宏NUM_PARAMS()，里面写NUM_PARAMS(abc,a,d,e)的话，替换后得到的结果要是4，能办到吗？<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
广告时间：<br />
<a href="http://blog.misakamm.org/p/209">http://blog.misakamm.org/p/209</a><br />
广告过后，回来精彩的节目<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
首先先介绍gcc派的解决方案：<br />
<pre><font color=#FF0000>#define </font><font color=#800080>PP_NARG</font>(...) <font color=#800080>PP_NARG_</font>(<font color=#800080>__VA_ARGS__</font>, <font color=#800080>PP_RSEQ_N</font>())
<font color=#FF0000>#define </font><font color=#800080>PP_NARG_</font>(...) <font color=#800080>PP_ARG_N</font>(<font color=#800080>__VA_ARGS__</font>)
<font color=#FF0000>#define </font><font color=#800080>PP_ARG_N</font>( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16, N, ...) N
</font><font color=#FF0000>#define </font><font color=#800080>PP_RSEQ_N</font>() \
16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0</pre>
非常漂亮巧妙又简洁的方案，我想不用我多解释了吧？<br />
不过，请注意，这是gcc的方案，以上代码放在vc8/vc9/vc2010等都会得不到正确的结果的，这个和vc的宏处理方式有关<br />
接下来就是给出vc的解决方案（以下均以vc2008和vc2010为准）<br />
<pre><font color=#FF0000>#define </font><font color=#800080>BRACKET_L</font>() (
<font color=#FF0000>#define </font><font color=#800080>BRACKET_R</font>() )
<font color=#FF0000>#define </font><font color=#800080>PP_NARG</font>(...) <font color=#800080>\
PP_NARG_ </font>( <font color=#800080>__VA_ARGS__</font>, <font color=#800080>PP_RSEQ_N</font>() )
<font color=#FF0000>#define </font><font color=#800080>PP_NARG_</font>(...) <font color=#800080>\
PP_ARG_N BRACKET_L</font>() <font color=#800080>__VA_ARGS__ BRACKET_R</font>()
<font color=#FF0000>#define </font><font color=#800080>PP_ARG_N</font>( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16, N,...) N
</font><font color=#FF0000>#define </font><font color=#800080>PP_RSEQ_N</font>() \
16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
</pre>
这里很特别的一点是对部分小括号做了替换。<br />
问题在于PP_NARG_到PP_ARG_N做参数传递的时候，如果已有显式的括号，那么不对里面的宏做展开计算参数个数，仅直接按显式的逗号个数判断出参数个数，从而导致__VA_ARGS__被当成一个参数传入。而把括号用宏替换掉后，则不出现直接的括号，就先对宏做展开，而展开后，再展开新构造出来的宏，这样才能让参数匹配上。<br />
不过gcc里面不能这么干，gcc会把宏名展开出来后，如果发现后面的符号并不是显式的括号，则把前面的宏符号化，不再展开。这两种不同的特性让我现在还不知道怎么编写宏能让两派都能兼容，正确展开出我想要的东西。<br />
解释了两个编译器的不同点以后，后面不再解释相同的问题，而会同时给出两份代码。<br />
另一个类似的问题，就是既然有不定个数的参数，如果我希望对每个参数都做一些处理，那如何做呢？<br />
举例，实现一个宏#define SPREAD(...)，要把参数里的东西连结成一个字符串<br />
之前的例子里，已经实现了把不定参数展开的手段，现在我们来尝试递归下降式展开（gcc版本）：<br />
#define SPREAD0( arg ) #arg<br />
#define SPREAD1(arg, ...) SPREAD0(arg)<br />
#define SPREAD2(arg, ...) SPREAD0(arg) SPREAD1(__VA_ARGS__,)<br />
#define SPREAD3(arg, ...) SPREAD0(arg) SPREAD2(__VA_ARGS__,)<br />
#define SPREAD4(arg, ...) SPREAD0(arg) SPREAD3(__VA_ARGS__,)<br />
#define SPREAD5(arg, ...) SPREAD0(arg) SPREAD4(__VA_ARGS__,)<br />
#define SPREAD6(arg, ...) SPREAD0(arg) SPREAD5(__VA_ARGS__,)<br />
#define SPREAD7(arg, ...) SPREAD0(arg) SPREAD6(__VA_ARGS__,)<br />
#define SPREAD8(arg, ...) SPREAD0(arg) SPREAD7(__VA_ARGS__,)<br />
#define SPREAD9(arg, ...) SPREAD0(arg) SPREAD8(__VA_ARGS__,)<br />
#define SPREAD(...) SPREAD9(__VA_ARGS__)<br />
在这里，每进入一层，就从__VA_ARGS__拆解一个最前面的参数出来，把剩下的参数给下一层<br />
这里有一个细节是__VA_ARGS__后面有一个逗号，意思就是补一个空参数，避免后面参数不足<br />
然后就可以用puts(SPREAD(1, 2, 3, 4));来测试了<br />
当然，还要使用前文的方式处理一下（gcc版）：<br />
#define SPREAD0( arg ) #arg<br />
#define SPREAD1(n, arg, ...) SPREAD0(arg)<br />
#define SPREAD2(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )<br />
#define SPREAD3(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )<br />
#define SPREAD4(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )<br />
#define SPREAD5(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )<br />
#define SPREAD6(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )<br />
#define SPREAD7(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )<br />
#define SPREAD8(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )<br />
#define SPREAD9(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )<br />
#define SPREAD(...) SPREAD9 ( 9, __VA_ARGS__ )<br />
vc版：<br />
#pragma warning(disable:4003) // 去除警告<br />
#define SPREAD0( arg ) #arg<br />
#define SPREAD1(n, arg, ...) SPREAD0(arg)<br />
#define SPREAD2(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()<br />
#define SPREAD3(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()<br />
#define SPREAD4(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()<br />
#define SPREAD5(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()<br />
#define SPREAD6(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()<br />
#define SPREAD7(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()<br />
#define SPREAD8(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()<br />
#define SPREAD9(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()<br />
#define SPREAD(...) SPREAD9 BRACKET_L() 9, __VA_ARGS__, BRACKET_R()<br />
以上只是模糊方式展开，因为参数个数不知道，后面会遇到宏参数为空的情况，于是vc编译器给出了警告<br />
如果把之前说的过技巧，就是分析出不定参数个数的宏，与这个结合，将产生更大的威力，我们可以实现精确展开，就是在SPREAD宏的定义里，有9的地方使用宏PP_NARG(__VA_ARGS__)替换一下，于是__VA_ARGS__后面的逗号可以去掉，也可以简化一些代码了，也能避免展开后有你所不希望的多余字符出现。<br />
<font color=#0000FF>测试考题1：<br />
定义一宏#define printf，让它能把printf(str, a, b, c);替换成std::cout&lt;&lt;a&lt;&lt;b&lt;&lt;c&lt;&lt;std::endl;<br />
参数个数不确定，不用考虑str的内容，但假设不多于10个参数</font><br />
<a href="http://blog.misakamm.org/p/209">http://blog.misakamm.org/p/209</a><br />
宏的威力还不止至此，当宏与C++模板编程结合的时候，真正的可怕就来临了。。。<br />
<font color=#0000FF>测试考题2：<br />
在C++0x之前，模板还没有不定参数，于是需要多个参数的时候，不得不手工解决，或者聪明的人，使用模板来生成多参模板代码。尝试一下这么做，看看和之前的问题难度加大在哪里。比如生成一个名为sum的模板函数，能接受1 - 10个参数，返回这些参数的相加的结果</font><br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
文章附带：<br />
第一考题参考答案：<br />
<pre><font color=#FF0000>#define </font><font color=#800080>BINARY_E0</font>(n, arg) <font color=#800080>TOSTRING </font>( arg )
<font color=#FF0000>#define </font><font color=#800080>BINARY_E1</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 0) )<font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1) )
<font color=#FF0000>#define </font><font color=#800080>BINARY_E2</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 0) )<font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1) )
<font color=#FF0000>#define </font><font color=#800080>BINARY_E3</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 0) )<font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1) )
<font color=#FF0000>#define </font><font color=#800080>BINARY_E4</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 0) )<font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1) )
<font color=#FF0000>#define </font><font color=#800080>BINARY_E5</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 0) )<font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1) )
<font color=#FF0000>#define </font><font color=#800080>BINARY_E6</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 0) )<font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1) )
<font color=#FF0000>#define </font><font color=#800080>BINARY_E7</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 0) )<font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1) )
<font color=#FF0000>#define </font><font color=#800080>BINARY_E8</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 0) )<font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) ( <font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1) )
<font color=#FF0000>#define </font><font color=#800080>BINARY_ENUM</font>(n) <font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, n) ( n, )
<font color=#FF0000>#define </font><font color=#800080>BIN_0</font>(n, arg) <font color=#800080>TOSTRING </font>( arg )
<font color=#FF0000>#define </font><font color=#800080>BIN_1</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BIN_</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), arg) <font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1))
<font color=#FF0000>#define </font><font color=#800080>BIN_2</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BIN_</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), arg) <font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1))
<font color=#FF0000>#define </font><font color=#800080>BIN_3</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BIN_</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), arg) <font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1))
<font color=#FF0000>#define </font><font color=#800080>BIN_4</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BIN_</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), arg) <font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1))
<font color=#FF0000>#define </font><font color=#800080>BIN_5</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BIN_</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), arg) <font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1))
<font color=#FF0000>#define </font><font color=#800080>BIN_6</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BIN_</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), arg) <font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1))
<font color=#FF0000>#define </font><font color=#800080>BIN_7</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BIN_</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), arg) <font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1))
<font color=#FF0000>#define </font><font color=#800080>BIN_8</font>(n, arg) <font color=#800080>MACROCAT</font>(<font color=#800080>BIN_</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), arg) <font color=#800080>\
</font><font color=#FF00FF>"," </font><font color=#800080>MACROCAT</font>(<font color=#800080>BINARY_E</font>, <font color=#800080>DECVAL</font>(n)) (<font color=#800080>DECVAL</font>(n), <font color=#800080>MACROCAT</font>(arg, 1))
<font color=#FF0000>#define </font><font color=#800080>BIN_ENUM</font>(n) <font color=#FF00FF>"0" </font><font color=#800080>MACROCAT</font>(<font color=#800080>BIN_</font>, n) ( n, )
测试代码：puts(BIN_ENUM(8));
测试考题不提供答案。
</pre>
</font>
<img src ="http://www.cppblog.com/misakamm/aggbug/164258.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/misakamm/" target="_blank">御坂美琴</a> 2012-01-16 16:22 <a href="http://www.cppblog.com/misakamm/archive/2012/01/16/164258.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>傻瓜学习C语言进制转换</title><link>http://www.cppblog.com/misakamm/archive/2011/12/27/162883.html</link><dc:creator>御坂美琴</dc:creator><author>御坂美琴</author><pubDate>Mon, 26 Dec 2011 17:41:00 GMT</pubDate><guid>http://www.cppblog.com/misakamm/archive/2011/12/27/162883.html</guid><wfw:comment>http://www.cppblog.com/misakamm/comments/162883.html</wfw:comment><comments>http://www.cppblog.com/misakamm/archive/2011/12/27/162883.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/misakamm/comments/commentRss/162883.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/misakamm/services/trackbacks/162883.html</trackback:ping><description><![CDATA[<font class="Apple-style-span" face="monospace"><span class="Apple-style-span" style="white-space: pre;"><div>来源<a href="http://blog.misakamm.org/p/190">http://blog.misakamm.org/p/190</a></div>
<div><span style="color: #444444; font-family: Helvetica, 'Helvetica Neue', Arial, sans-serif; font-size: 13px; line-height: 26px; white-space: normal; ">
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">最近受网友的邀请，就写一篇入门的教学文章。不过对于已经有一定实力水平的人来说，入门级的东西反而对于他（她）们来说不容易解释清楚，我也想挑战一下自己，看看我能把一些基础问题怎么解释能让智商80的人也能看明白（虽然这样说有点夸张），所以文章的标题就叫做&#8220;傻瓜学习C语言进制转换&#8221;，不过也不是没有要求的，要求是，看本文的时候，请你一定要按顺序看，并且要确定你会写这样一个c程序：输入一个int，分解出它的个位，十位，百位（提示：要用&#8217;%'求模运算和&#8217;/'整除运算）。。。。。。<br />
<br />
1.数值与进制<br />
数值与进制是两个不同的东西。数值是什么？100，200这种并不是数值。<br />
什么是数值？古代的时候，人们记数，有一个物品就记一块石头，或者有11个物品，就打11个绳结，这就是数值，如果你要表示1000，那你还真的需要打1000个绳结来表示。可问题就是，这样子你会累死，为了不用累死，于是他们发明了另一种表达方式：准备两种不同的石头A和B，有一个，那就用一块A石头表示，两个就用两块A表示，如果太多了，比如10个，就用一块B石头来表示有10个A石头，比如AAAAABB表示25个。但是，如果表达的数值更大，那就再多准备一种石头C，每10个B石头就用一个C石头来表示，比如AABCC表示212。于是，这样就可以大大减少所需要的石头的数目，而这，就是进制。<br />
进制是数值的一种表示方式</p>
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">2.数值与进制的转化<br />
进制是为了表示一个数值，如果是每10个进一，那么就是我们熟悉的10进制，否则，如果是每k个进一，那就是k进制，比如我们的时间，是每60秒记1分钟，这就是60进制。然后，怎么把一个用特定进制的表示，得到它的数值呢？很简单，比如上文说的AABCC，首先，有两个A，分解得AA + BCC，就是2 + BBC，然后，1个B就是10个A，于是就是AA + AAAAAAAAAA + AAAAAAAAAA + C，然后一个C等于10个B，就是AA + AAAAAAAAAA + AAAAAAAAAA + BBBBBBBBBB，这样一直拆下去，直到全部是A为止，你就得到实际的数值了，实际的数值就是A的个数，记住这一点，数值和进制是两码事。</p>
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">然后，反过来，怎么把一个数值转化为特定进制呢？很简单，按照进制的定义，比如现在有数值AAAAAAAAAAAAAAAAAAAAAAA，然后，要转成10进制，那么我们每10个分一分组：AAAAAAAAAA + AAAAAAAAAA + AAA，然后，把10个A用B来表示，得到：B + B + AAA，如果B有10个，那再把它换成C表示。那么，如果你明白了以上方法，你就得到一个最基本的进制转换手段，就是先化为数值，再重新用另一个进制表示。比如10进制的13，要化成二进制，那么，就是BAAA -&gt; AAAAAAAAAAA -&gt; AA AA AA AA AA AA A 这时，换一个符号，每两个A用一个M表示，那么就是MM MM MM A，再每两个M用一个N表示，得到NN N A，再每两个N用一个P表示，得到P N A。而在这里，一个P等于8个A，一个N等于4个A，所以P + N + A你可以验算出8 + 4 + 1，等于原来的数值。而这种表示方法，就和罗马数字很相似，罗马数字里，用I表示1，用V表示5，用X表示10，于是18就用XVIII表达，用一个字母多次重复来表达一个数值。</p>
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">后来，为了能更方便书写，因为字母数量是有限的，无法表达更大的数字，书写方式改用阿拉伯数字写在不同的位置来表达，于是就是我们今天的10进制数字。比如刚刚的例子，BAAA，有一个B，于是在十位写1，然后有三个B，在个位写3，也就是B的个数，组合起来就是13，这是十进制的情况。如果是二进制，刚刚我们得到的结果是PNA，注意这里没有M，相当于0个，而如果我们用二进制写，那就有四个位，个位有一个A，记1，第二位相当于M的个数，是0，组合起来是01，第三位是N，记1，组合是101，第四位有一个P，再组合就是1101，于是这就是10进制的13，化为二进制的结果。</p>
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">3.进制的特点<br />
问你，3638除以10的余数是多少？给你一秒种思考时间，多少？如果这个你不能马上说出来，那你就要反省了。结果应该是8，直接看个位不就对了。那么3638除以10的商呢？再给你一秒。。。。。。。。。。。。。。。。。。。这个答错的话要重读小学了，答案当然是363.8，如果把这个数取整，不要小数部分，那就是363。小学的时候你就应该知道，对一个数乘以10或者除以10这种计算是超简单的，因为我们用的是10进制。类似的，问一下你，经过60个5秒是多少分钟多少秒？再给你一秒思考时间，要毫不犹豫的回答我。你可别去计算60*5=300，这是多余的。答案是5分钟，时间我们用的60进制，那么乘以60只要改一下单位就足够了，肯定是对的。再问你，10分钟分成60份是多少秒？你必须立即回答我是10秒。<br />
我们推广到任意k进制，按这个特点，k进制下，乘以k或者除以k的运算是超级简单的，比如8进制的123，乘以8肯定是1230，除以8就是12.3，相当于在移动小数点而已。于是，k进制下乘除k就是移动小数点。而除以k求余数的话，像刚刚的8进制的123除以8，就等于12余3，就是要得到个位上的数字，同时得到原来的数舍弃掉个位的结果。这个性质非常的重要！除法的本质是什么？其实除以k是得到被除数在k进制下的个位数（余数），和小数点左移的结果（商）。</p>
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">4. C下实现数值转化为进制<br />
好，现在回到程序，给你一个int n，要把它的各位上的数字取出来，按上面的性质，那就很简单了，先得到个位，n%10（这个&#8217;%'是求余运算），然后小数点左移，n = n / 10; 然后不断循环这个过程，如下代码：</p>
<pre><font color="#0000FF">int </font>n = 2456;
<font color="#0000FF">while </font>(n &gt; 0)
{
    <font color="#FF0000">printf</font>(<font color="#FF00FF">"%d,"</font>, n % 10);
    n = n / 10;
}</pre>
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">输出结果是&#8221;6,5,4,2,&#8221;，好好领悟一下这段代码。给你五分钟时间。每一次%10就是取出个位，每一次/10就是丢掉个位。<br />
而如果把输出的结果里的数字，逆过来看，就是&#8221;2456&#8243;。</p>
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">在这里，那个int所表示的，就是一个数值，刚刚我们的代码所做的事就是把这个数值，一位一位的分解出来。<br />
而事实上，这个过程就是把数值转化为特定进制的过程。刚刚就是把数值转化为10进制。<br />
如果把刚刚的代码改为：</p>
<pre><font color="#0000FF">int </font>n = 13;
<font color="#0000FF">while </font>(n &gt; 0)
{
    <font color="#FF0000">printf</font>(<font color="#FF00FF">"%d,"</font>, n % 2);
    n = n / 2;
}</pre>
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">没错，输出结果是&#8221;1,0,1,1&#8243;，就是刚刚把13化为二进制的例子，每一次%2就是取出二进制下的个位，每一次/2就是丢掉二进制下的个位。<br />
只要把那个次序反过来，就得到1101，就是13化为二进制的结果。在你真正搞明白了除法的本质后，那么，数值转化为以k进制表示那是一件很简单的事。</p>
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">5. 进制转化为数值<br />
这部分我不打算讲，很多人对这个比起前面的内容来说容易理解很多，直接用进制的定义就已经很好办了，没什么太难理解的东西。</p>
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">6. 作业<br />
编写一个程序，输入三个整数n A B，表示把A进制的n，转换为B进制，并输出。<br />
样例：<br />
输入 输出<br />
11 8 10 9<br />
129 10 2 10000001<br />
22 3 6 12</p>
<p style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-shadow: #fbfbfb 0px 1px 0px; ">假定输入的A和B都在2-10这个范围，超出范围的不用去处理，输入的n保证在int范围内。</p>
</span></div>
</span></font><img src ="http://www.cppblog.com/misakamm/aggbug/162883.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/misakamm/" target="_blank">御坂美琴</a> 2011-12-27 01:41 <a href="http://www.cppblog.com/misakamm/archive/2011/12/27/162883.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>