FREE Subscription to Dr. Dobb’s Digest: Same Great Content, New Digital Edition
Site Archive (Complete)
C++ Blog: A stopped clock is right twice a day
C++
void main(void)

Calls, Returns and In-Between.

by Kevin Carlson
SELECTIVE IGNORANCE

Finding the Signal in the Noise

by Andrew Koenig
April 09, 2007

A stopped clock is right twice a day

Which is better, a program that works most of the time or one that fails most of the time?

Faced with this question, most people would probably prefer the program that mostly works. But what if mostly working isn't good enough? Then the program that mostly fails is easier to fix.

Let's make this discussion concrete. A recent Usenet discussion centered on writing a function to return a const char*:

const const char* foo() {
    char result[100];
    // ...
    return result;
}

This strategy obviously doesn't work: result is a local variable, so by the time the caller gets around to doing anything with the pointer returned, it points at memory that is long gone.

Obviously the program's author could dynamically allocate the memory instead:

const const char* foo() {
    char* result = new char[100];
    // ...
    return result;
}

Now the pointer returned is valid. In exchange, the caller has to take responsibility for freeing the memory if the program is to avoid memory leaks.

Is there a strategy that combines the convenience of not having to worry about freeing memory with the requirement to return a valid pointer? Of course there is. Let the pointer stick around until the next call:

const const char* foo() {
    static char* result;
    delete[] result;
    result = new char[100];
    // ...
    return result;
}

Now the caller doesn't have to delete the memory, but it goes away automatically at the next call. However, we now have a function that will fail if used twice in the same expression:

    bar(foo(), foo());

Here, whichever call to foo is executed second will wipe out the memory addressed by the pointer that the first call returned. In other words, bar will receive one valid pointer and one invalid one.

We can solve this problem too: Instead of remembering the last pointer returned, we can remember the last n for a suitable value of n.

Now we have transformed an uncommon problem into a dangerously obscure one. The trouble is that users of foo will think it is safe to use the value returned indefinitely; but sooner or later someone is going to misuse it An attempt to make things easier has pushed the problem into the background where it is harder to detect.

When you have a choice between a program that fails subtly and one that fails spectacularly, you are often better off with the one that fails spectacularly.

Posted by Andrew Koenig at 02:33 PM  Permalink




 
INFO-LINK