In a nutshell, a register variable should be stored in a register and a volatile variable should be kept in the memory. The "volatile" qualifier tells the compiler not to optimize that variable, i.e. leaving it as it is. Here is a more straight forward example to demonstrate their differences.
1: #include <setjmp.h>
2: #include <stdio.h>
3: #include <stdlib.h>
4:
5: static jmp_buf jmpbuffer;
6:
7: int main(int argc, char *argv[]) {
8: int a;
9: register b;
10: volatile c;
11:
12: if (argc < 4) {
13: printf("usage: %s a b c\n", argv[0]);
14: exit(1);
15: }
16:
17: a = atoi(argv[1]);
18: b = atoi(argv[2]);
19: c = atoi(argv[3]);
20:
21: printf("Before longjmp: a = %d, b = %d, c = %d\n", a, b, c);
22: if (setjmp(jmpbuffer) != 0) {
23: printf("After longjmp: a = %d, b = %d, c = %d\n", a, b, c);
24: return 0;
25: }
26:
27: a++;
28: b++;
29: c++;
30: longjmp(jmpbuffer, 1);
31: return 0;
32: }
This small piece of code takes three integers as input, increments them and then calls a longjmp(). The value of these three variables are printed before and after the longjmp() call to show what is going on.
First, compile the code:
gcc test_jmp.c -o test_jmp
Then run the code:
./test_jmp 1 2 3
which prints the results:
Before longjmp: a = 1, b = 2, c = 3
After longjmp: a = 2, b = 3, c = 4
Not get it? What you see here is that after the longjmp(), the changes made to these three variables are kept although the code "jumps" to the place where you called setjmp(). But this is only the first half of the story. Let's make gcc optimize the code.
Compile the code again:
gcc test_jmp.c -o test_jmp -O
Run the code with the same inputs:
./test_jmp 1 2 3
which gives you the following results:
Before longjmp: a = 1, b = 2, c = 3
After longjmp: a = 1, b = 2, c = 4
What happened? a and b are restored to their values when setjmp() is called, i.e. the changes made to them after setjmp() disappeared. Only the volatile variable c does not roll back.
Myth dispelled: When you compile the code without optimization, all the variables are kept in memory. The register qualifier of variable b is ignored. When you compile it with optimization, both the auto variable a and register variable b are kept in registers and setjmp/longjmp is required to restore all the values stored in registers. For the volatile variable c, since we explicitly told the compiler not to optimize it, it stays in the memory thus all the changes made to it are kept after the longjmp().
Another note is that the auto variable a may not be pushed to the register, because its behavior is implementation dependent although the gcc we used in the above example does it. Therefore if you really want to make sure a variable is stored in a register, you have to use the "register" qualifier.
What's wrong with the example in APUE, i.e. program 7.5? Although you observe similar outputs, the things under the hood are different today. Compilers are very aggressive to make various optimizations. In program 7.5, the variables get assigned with values in the code (known at compile time) instead of user input (known at runtime). Therefore when you ask for optimization, the compiler goes ahead to replace the variables in the output code with their values directly. In other words, there is no register/memory storage issue in that particular example any more. Compilers have advanced a lot since when APUE was written.