C99의 va_copy
variadic argument API에 추가 하면 Turing-completeness의 뒷문이 될 수 있습니다. 원래 인수를받은 함수가 아닌 함수에서 가변 인수 목록을 두 번 이상 반복 va_args
할 수 있으므로 포인터없는 포인터를 구현하는 데 사용할 수 있습니다.
물론, variadic 인수 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
.