Programmers who feel more comfortable with C and assembly than with lambda calculus might find the following description helpful: call/cc captures the function call stack that was in effect when it was invoked, and encapsulates it in an object that can be passed around in the program, later to be reinstated. It saves only the stack (and conceptually the processor state, although in practice that can be kept implicit) - the state of of memory and i/o is unaffected by an invocation of a continuation.

Of course, since the actual stack changes all the time, this typically involves making a copy of it, rather than just a pointer. This is expensive, so Scheme implementations typically try to minimize it, for example by storing activation records in a linked list of small stacks, or (in the extreme case) on the heap with no stack at all.

This facility is precisly what is needed to implement various language features:

  • Non-local returns (C's setjmp/longjmp), where a procedure skips some part of the call stack and returns to a procedure higher up, can be trivially implemented by capturing the stack before invoking a series of function calls. (see Teach Yourself Scheme: 13.2 Escaping continuations)
  • C++-style exceptions is the above non-local returns, combined with a list of places to return to when various things are thrown, so call/cc goes a long way towards implementing them too.
  • nondeterminism and backtracking can eloquently be expressed at the langage level by allowing a single function invocation to return several times, until a suitable value has been found. (see Teach Yourself Scheme: 14 Nondeterminism)
  • Multiprocessing, allowing several different computations to apparently take place simultaneously, is often implemented on a uni-processor system by storing more than one processor state and function call stack, and periodically switch which one the processor actually uses (so-called threads of control). Since this is precisly what call/cc captures, one can implement multiprocessing on top of it. Co-operative threads only takes a few lines of code; for preemtive threads you also need a way to respond to timer interrupts. (see Teach Yourself Scheme: 15 Engines)

C's closest counterpart to call/cc is the setjmp/longjmp facility, which marks a place in the call stack but does not save a copy of it. Therefore, you cannot use these macros to restore a stack state once the function it was saved in has returned, so they are not as useful. You can implement threads in terms of setjmp/longjmp, but in that case you need some non-portable code to set up multiple stacks to switch between.