const unsigned char table[]={Here's a similar table-driven program that reads two entries in the table each time around the loop. The first table entry is treated as a repetition count, and the second table entry is the letter to repeat. Again, the program stops when it hits a repetition count of zero:
20,
20,
2,
2,
2,
20,
20,
0 /* end of the table */
};
int foo(void) {
int i=0; /* location in the table */
while (table[i]!=0) { /* print one entry in the table */
int n=table[i++]; /* number of times to print */
char c='#'; /* character to print */
for (int repeat=0;repeat<n;repeat++) /* print it n times */
std::cout<<c<<" ";
std::cout<<std::endl;
}
return 0;
}
const unsigned char table[]={Note that the above is just a nine-entry table; the relationship between 3 and 'q' is purely conceptual.
/* --- n, character to print n times ---- */
3,'q',
2,'@',
4,'~',
1,'z',
0 /* end of the table */
};
int foo(void) {
int i=0; /* location in the table */
while (table[i]!=0) { /* print one entry in the table */
int n=table[i++]; /* number of times to print */
char c=table[i++]; /* character to print */
for (int repeat=0;repeat<n;repeat++) /* print it n times */
std::cout<<c<<" ";
}
return 0;
}
int x=0; std::cin>>x; /* read what to do */If all the "if" statements are testing the same integral value, a "switch" does the same thing:
if (x==3) std::cout<<"Lucky three!\n";
else if (x==7) std::cout<<"Seven! Yes!\n";
else if (x==13) std::cout<<"NooooOOOO!!!!\n";
else std::cout<<"meh\n";
int x=0; std::cin>>x; /* read what to do */Often "switch" is faster than a nested block of "if" statements, especially if there are many possibilities. I'm using "switch" below, so I thought I'd explain this syntax first.
switch (x) {
case 3: std::cout<<"Lucky three!\n"; break;
case 7: std::cout<<"Seven! Yes!\n"; break;
case 13: std::cout<<"NooooOOOO!!!!\n"; break;
default: std::cout<<"meh\n"; break;
}
const unsigned char table[]={
0, /*yo! */
1, /*print x */
1, /*print x */
0, /*yo! */
2 /* exit */
};
int foo(void) {
int i=0; /* our location in the table */
while (1) /* always keep looping through the table */
switch (table[i++]) { /* look at the next thing in the table */
case 0: cout<<"Yo!\n"; break; /* single-Yo instruction */
case 1: cout<<"x\n"; break; /* single-X instruction */
case 2: return 0; /* stop looping through the table */
default:
cout<<"Unrecognized table entry!\n";
return -999;
}
}
Rather than having two identical "print x" commands, we can make the "x" command repeatable, by adding a repetition count.
const unsigned char table[]={Note that 0, a "Yo!" instruction, stands alone in the table, while 1, a "multi-x" instruction, takes two bytes, because the second byte is an x count. The indented "2" is not an exit command, it's the repetition count for the 1 instruction!
0, /*yo! */
1, /*print x... */
2, /* ... two times */
0, /*yo! */
2 /* exit */
};
int foo(void) {
int i=0; /* our location in the table */
while (1) /* always keep looping through the table */
switch (table[i++]) { /* look at the next thing in the table */
case 0: cout<<"Yo!\n"; break; /* single-Yo instruction */
case 1: { /* multi-x instruction */
int count=table[i++]; /* next byte in table is the x repeat count */
for (int repeat=0;repeat<count;repeat++)
std::cout<<'x'<<endl;
break;
}
case 2: return 0; /* stop looping through the table */
default:
cout<<"Unrecognized table entry!\n";
return -999;
}
}
const unsigned char table[]={
0xb0, /*set x = ... */
7, /* ... this byte */
0xc3 /* exit */
};
int foo(void) {
int x=0; /* our "register" (temporary storage, and return value) */
int i=0; /* our location in the table */
while (1) /* always keep looping through the table */
switch (table[i++]) { /* look at the next thing in the table */
case 0xb0: { /* set-x instruction */
x=table[i++]; /* next byte is the new value for x */
break;
}
case 0xc3: return x; /* stop looping through the table */
default:
cout<<"Illegal instruction!\n";
return -999;
}
}
Our table just has (8-bit) bytes in it, but sometimes we want to be
able to set an entire (32-bit) int. The standard x86 solution to
this is to split the integer into four bytes: first the low byte
(lowest value, last two hex digits), then the not-so-low byte, the
not-so-high byte, and the highest byte, like so.
const unsigned char table[]={
0xb8, /* set x =... */
4, /* low byte is 4 (that is, 0x04) */
1, /* next byte is 1 (that is, 0x01) */
0, /* highest two bytes are both zero */
0,
0xc3 /* return that */
};
int foo(void) {
int x=0; /* register */
int i=0;
while (1) switch (table[i++]) {
case 0xb8:
x=table[i]|(table[i+1]<<8)|(table[i+2]<<16)|(table[i+3]<<24);
i+=4;
break;
case 0xc3: return x;
default:
cout<<"Illegal instruction!\n";
return -999;
}
}
This returns "0x104". The "0x04" is the low byte. "0x01" is the next higher byte, and all higher bytes are zero.