有没有什么工具或是编译器、方法之类的能自动替换宏定义?
是有一个程序为了尽可能节省字符数量用了大量宏定义,我想看懂它,但全是宏实在看不明白
#define R { return
#define P P (
#define L L (
#define T S (v, y, c,
#define C ),
#define X x)
#define F );}
int r, a;
P y, X
R y - ~y << x;
}
Z (X
R r = x % 2 ? 0 : 1 + Z (x / 2 F
L X
R x / 2 >> Z (x F
#define U = S(4,13,-4,
T t)
{
int
f = L t C
x = r;
R
f - 2 ?
f > 2 ?
f - v ? t - (f > v) * c : y :
P f, P T L X C
S (v+2, t U y C c, Z (X )))
:
A (T L X C
T Z (X ) F
}
A (y, X
R L y) - 1
? 5 << P y, X
: S (4, x, 4, Z (r) F
#define B (x /= 2) % 2 && (
D (X
{
int
f,
d,
c = 0,
t = 7,
u = 14;
while (x && D (x - 1 C B 1))
d = L L D (X ) C
f = L r C
x = L r C
c - r || (
L u) || L r) - f ||
B u = S (4, d, 4, r C
t = A (t, d) C
f / 2 & B c = P d, c C
t U t C
u U u) )
C
c && B
t = P
~u & 2 | B
u = 1 << P L c C u) C
P L c C t) C
c = r C
u / 2 & B
c = P t, c C
u U t C
t = 9 );
R a = P P t, P u, P x, c)) C
a F
}
main ()
R D (D (D (D (D (99)))) F
PS:这是一个在假设无限大内存没有整形上限的情况下,输出一个巨大的数字的程序,这个数字非常大,远远超越想象。所以我想知道生成这个数字的D函数的工作原理
宏展开大概是这样子吧。标准C必须要有返回类型,我加上int为返回类型。
但是貌似逻辑有问题,运行出错!
#include<stdio.h>
int r, a;
int P(y, x)
{
return y - ~y << x;
}
int Z(x)
{
return r = x % 2 ? 0 : 1 + Z(x / 2);
}
int L(x)
{
return x / 2 >> Z(x);
}
int S(v, y, c, t)
{
int
f = L(t),
x = r;
{
return f - 2 ? f > 2 ? f - v ? t - (f > v) * c : y : P(f, P(S(v, y, c, L(x)), S(v + 2, t = S(4,13,-4, y), c, Z(x))))
: A(S(v, y, c, L(x)),
S(v, y, c, Z(x)));
}
}
int A(y, x)
{
return L(y) - 1
? 5 << P(y, x)
: S(4, x, 4, Z(r));
}
int D(x)
{
int
f,
d,
c = 0,
t = 7,
u = 14;
while (x && D(x - 1), (x /= 2) % 2 && ( 1))
d = L ( L ( D (x) ) ),
f = L ( r ),
x = L ( r ),
c - r || (
L ( u) || L ( r) - f ||
(x /= 2) % 2 && ( u = S (4, d, 4, r ),
t = A (t, d) ),
f / 2 & (x /= 2) % 2 && ( c = P ( d, c ),
t = S(4,13,-4, t ),
u = S(4,13,-4, u) )
),
c && (x /= 2) % 2 && (
t = P (
~u & 2 | (x /= 2) % 2 && (
u = 1 << P ( L ( c ), u) ),
P ( L ( c ), t) ),
c = r ),
u / 2 & (x /= 2) % 2 && (
c = P ( t, c ),
u = S(4,13,-4, t ),
t = 9 );
{
return a = P(P(t, P(u, P(x, c))),
a);
}
}
int main()
{
return D(D(D(D(D(99)))));
}
首先,这个代码真是666
在 C/C++ 中,你可以使用 cpp 命令来自动替换宏定义。
例如,假设你有如下的代码文件 main.cpp:
#define PI 3.14
#define SQUARE(x) ((x) * (x))
int main() {
int r = 3;
double area = PI * SQUARE(r);
return 0;
}
你可以使用如下命令来自动替换宏定义:
cpp main.cpp
会输出:
int main() {
int r = 3;
double area = 3.14 * ((r) * (r));
return 0;
}
这是代码原作者写的另一个方便阅读的版本,它和问题中的原代码功能一样,有感兴趣的也可以看看,这个程序真的很有意思,它能生成的数字真的大到让“超越想象”这个描述苍白无力
typedef int Tree;
typedef int INT;
typedef int TREE;
typedef int BitStream;
#define DESCEND xx
Tree lastRight, accumulate;
// A bijective pairing.
TREE Pair (TREE yy, TREE xx)
{
// x - ~x = x - (-1 - x) = 2 * x + 1
return yy - ~yy << xx;
}
// The second component of a pair.
TREE Right (TREE xx)
{
return lastRight = xx % 2 ? 0 : 1 + Right (xx / 2);
}
// The first component. Note that we leave the other component in lastRight.
TREE Left (TREE xx)
{
return xx / 2 >> Right (xx);
}
// Encoding
// PI(A,B) = Pair(0,Pair(A,B))
// LAMBDA(A,B) = Pair(1,Pair(A,B))
// APPLY(A,B) = Pair(2,Pair(A,B))
// STAR = Pair(3,0) = 7
// BOX = Pair(3,1) = 14
// VAR(n) = Pair(4+2n,0) = 9 + 4n [n >= 0]
// The empty context is 0, and the context Gamma,A is Pair (A,Gamma).
// STAR and BOX are the only terms x with (x&2)!=0
// Increment the index of each variable in xx. Uses Subst.
// Making this a macro means that we can absorb an "=" and a "(" into the macro.
#define Lift(xx) Subst (4, 13, -4, xx)
// Substitute yy for vv in term, and normalise. Variables > yy get adjusted by
// -context. [The precise normalisation is: if yy and term are normal, and the
// substitution has a normal form, then the normal form is returned.]
TREE Subst (INT vv, TREE yy, INT context, TREE term)
{
Tree
aux = Left (term), // The operation of term.
xx = lastRight; // The body of term.
{
return
aux - 2 ?
aux > 2 ?
// Variable or Star or Box.
aux - vv ? term - (aux > vv) * context : yy :
// aux = 0 or aux = 1: lambda or pi. The stray 'term =' below is
// harmless, but allows us to push the '=' into the Lift macro.
Pair (aux, Pair (Subst (vv, yy, context, Left (xx)),
Subst (vv+2, term = Lift (yy), context, Right (xx))))
:
// Application. Use Apply.
Apply (Subst (vv, yy, context, Left (xx)),
Subst (vv, yy, context, Right (xx)));
}
}
// Apply yy to xx and normalise. [Precisely, if yy and xx are normal, and
// yy(xx) is normalisable, Apply(yy,xx) returns the normal form of yy(xx).
TREE Apply (TREE yy, TREE xx)
{
return Left (yy) - 1
// 5 << x == Pair(2,x)
? 5 << Pair (yy, xx)
: Subst (4, xx, 4, Right (lastRight));
}
// We use xx as a bit stream. The MAYBE macro tests the next bit for us.
#define MAYBE (xx /= 2) % 2 &&
// Derive parses a bit stream into terms of CoC and normalises everything. The
// outputs are accumulated into the variable yy. We also recurse, so as to
// cover all the BitStreams which are < xx.
TREE Derive (BitStream xx)
{
Tree
aux,
auxTerm,
// The axiom.
context = 0,
term = 7,
type = 14;
// Inside the while condition is the main recursion that makes us monotone.
// It doesn't need to be inside the while, but that allows us to compress the
// "),". It also means we get called more often, which makes "accumulate"
// bigger...
while (DESCEND && Derive (xx - 1), MAYBE (1))
// Get another term.
auxTerm = Left (Left (Derive (xx))),
// And get its type.
aux = Left (lastRight),
// And get the left-over bit-stream. This leaves the context from
// the sub-derivation in lastRight.
xx = Left (lastRight),
// Rules that depend on two antecedents... The two contexts (one is in
// lastRight) must be the same.
context - lastRight || (
// APPLY. type must be PI(aux,-).
Left (type) || Left (lastRight) - aux ||
MAYBE (type = Subst (4, auxTerm, 4, lastRight),
term = Apply (term, auxTerm)),
// Weakening. auxType must be STAR or BOX. The / 2 & MAYBE
// combines MAYBE with testing the correct bit of auxType. It is
// safe to do this immediately after an APPLY above, because APPLY
// does not change contexts.
aux / 2 & MAYBE ( context = Pair (auxTerm, context),
term = Lift (term),
type = Lift (type) )
),
context && MAYBE (
// If we get here, we are either going to do PI formation or LAMBDA
// introduction. PI formation requires type to be STAR or BOX. We
// allow LAMBDA introduction whenever the context is non-empty.
// This extension is a conservative extension of CoC.
term = Pair (
// Because of the && in MAYBE, this subexpression returns a
// boolean 1 if we're doing LAMBDA introduction, 0 if we're
// doing PI formation. The ~type&2| ensures that we do LAMBDA
// introduction if type is not the Star or Box needed to do PI
// formation.
~type & 2 | MAYBE (
// If we're doing lambda introduction on term, then we also
// need to do a PI formation on type. This is always
// non-zero. 1 << x = Pair(0,x).
type = 1 << Pair (Left (context), type)),
Pair (Left (context), term)),
// Remove the context item we just used.
context = lastRight ),
// If type is STAR or BOX then we allow variable introduction.
type / 2 & MAYBE (
context = Pair (term, context),
type = Lift (term),
term = 9 ); // Pair (4, 0)
{
// Pair term, type, context, and xx together, and chuck it all onto
// accumulate.
return accumulate = Pair (Pair (term, Pair (type, Pair (xx, context))),
accumulate);
}
}
TREE main ()
{
return Derive (Derive (Derive (Derive (Derive (99)))));
}