C99 va_copy
在可变参数API中的添加可能会给我们提供图灵完备性的后门。由于可以在一个函数(最初接收参数的函数除外)中多次遍历可变参数列表,va_args
因此可用于实现无指针指针。
当然,可变参量API的真正实现可能在某个地方有一个指针,但是在我们的抽象机中,它可以使用魔术来实现。
这是一个使用任意转换规则实现2堆栈下推自动机的演示:
#include <stdarg.h>
typedef struct { va_list va; } wrapped_stack; // Struct wrapper needed if va_list is an array type.
#define NUM_SYMBOLS /* ... */
#define NUM_STATES /* ... */
typedef enum { NOP, POP1, POP2, PUSH1, PUSH2 } operation_type;
typedef struct { int next_state; operation_type optype; int opsymbol; } transition;
transition transition_table[NUM_STATES][NUM_SYMBOLS][NUM_SYMBOLS] = { /* ... */ };
void step(int state, va_list stack1, va_list stack2);
void push1(va_list stack2, int next_state, ...) {
va_list stack1;
va_start(stack1, next_state);
step(next_state, stack1, stack2);
}
void push2(va_list stack1, int next_state, ...) {
va_list stack2;
va_start(stack2, next_state);
step(next_state, stack1, stack2);
}
void step(int state, va_list stack1, va_list stack2) {
va_list stack1_copy, stack2_copy;
va_copy(stack1_copy, stack1); va_copy(stack2_copy, stack2);
int symbol1 = va_arg(stack1_copy, int), symbol2 = va_arg(stack2_copy, int);
transition tr = transition_table[state][symbol1][symbol2];
wrapped_stack ws;
switch(tr.optype) {
case NOP: step(tr.next_state, stack1, stack2);
// Note: attempting to pop the stack's bottom value results in undefined behavior.
case POP1: ws = va_arg(stack1_copy, wrapped_stack); step(tr.next_state, ws.va, stack2);
case POP2: ws = va_arg(stack2_copy, wrapped_stack); step(tr.next_state, stack1, ws.va);
case PUSH1: va_copy(ws.va, stack1); push1(stack2, tr.next_state, tr.opsymbol, ws);
case PUSH2: va_copy(ws.va, stack2); push2(stack1, tr.next_state, tr.opsymbol, ws);
}
}
void start_helper1(va_list stack1, int dummy, ...) {
va_list stack2;
va_start(stack2, dummy);
step(0, stack1, stack2);
}
void start_helper0(int dummy, ...) {
va_list stack1;
va_start(stack1, dummy);
start_helper1(stack1, 0, 0);
}
// Begin execution in state 0 with each stack initialized to {0}
void start() {
start_helper0(0, 0);
}
注意:如果va_list
是数组类型,则实际上有指向函数的隐藏指针参数。因此,最好将所有va_list
参数的类型更改为wrapped_stack
。