Feeds

Variables Won't Constants Aren't:

A random walk through constant values in C++

Boost IT visibility and business value

I first read the lament to the capricity of programming in the title of this piece in Creative Computing, years before C was devised. But it’s still true that there is no way, not even one, of being absolutely sure that a value you set in C or C++ won’t change.

This is despite the fact that in the 30 years following the creation of C, C and C++ have acquired new keywords, features and libraries and have been generally enhanced to a point where my ancient battered copy of Kernighan and Richie hides in a cupboard and refuses to even sit on the desk where Visual C++ 2005 lives.

C++ gives the programmer a menu of methods to help him/her protect his/her data. At least, that’s what’s implied if you don’t read Stroustrup too closely. It’s not that you’re being lied to; it’s just that someone is being rather economical with the truth.

It’s tempting to think that, given:

const int MyConst = 42;
double MyVals[MyConst];
for (int i=0; i != MyConst; i++) {
    // ...
}

you won’t ever go off the end of the array. Tempting, but wrong; and it is quite trivial to make it happen:

int& x = (int&) MyConst;
x = 43;

The default in C++ is variables, not constants; and thus you probably won’t even get a warning if you do such a thing. And so you implicitly, but silently, cast away constness.

This is one of the many reasons that classical C style casts are frowned upon in most coding standards. But for most programmers these days, the only time that they ever review someone else’s code is at interview [and not always then – Ed] - so probably no-one will notice if they aren’t following standards.

The accepted, but still rather worrying, technique is:

int& CPlusCast = const_cast<int&>(MyConst);

[Am I the only one who thinks this cast has the wrong name? – Dominic]

OK, but when you’re hunting a bug, then you might well be tempted to think that this means that MyConst can’t change. Yet it might or might not mean that; and of course now that constness is gone, MyConst can be passed into a function with a variable parameter signature.

Even specifying:

void Increment(const int& x)

doesn’t mean it won’t ever change - as we’ve seen. More than that, since threads are coming back into mainstream programming, your MyConst value may change randomly at some random point during execution.

You can always try to specify that a function mustn’t change any class variables, which would be really handy if your class is supposed to be inherited from, but again, this can be changed later.

Const functions only protect members of the class, and the language also stops you getting around this by getting the const function to call a non-const member. But the syntactic protection does not extend to globals; although if you use them you’re not really into this level of discipline anyway.

And it just wouldn’t be C++ if there weren’t yet another workaround available. So, we can specify member variables as mutable, and thus make them prey to random change by the sort of people who think “const member function” actually means that only externally observable states should remain constant; and that the function should be free to meddle with its private housekeeping variables.

But sometimes you need to expose some internal structure like the STL string or MFC CString, so that you can use the character buffer with older code. STL does the right, but fallible, thing and so the c_str() member returns a const pointer; which at least takes some effort if you’re determined to corrupt it. MFC doesn’t even try.

STL also has const_iterators, which of course aren’t constant (because there wouldn’t be much point having an index that didn’t change); but the idiom is that you can’t directly change the values of the collection, which is both safer and potentially rather faster. An iterator that you know won’t change things can not only be cached, but is also rather easier to multithread.

Now, so far I’ve only complained about intentional changes to variables. We also have to live with the possibility of memory being trashed, since const is just a syntactic sugar coating for the bitter reality of the underlying memory model. Hence, in Visual C++ 2005, I can illustrate a simplified example of a const value being trampled on simply for being too close to another on the stack:

void HeisenBug(void) {
    char a = 42;
    char Bug[5];
    strcpy(Bug, "123456789abcdefghijk");
}

Note that this mucks up the value “before” the Bug array; and that this is compiler-dependent. This effect may be anything from benign to subtly malicious.

Of course, if you’re in the IDE of a decent debugger like Visual Studio 2005, then this effect will be caught; but if you’re in production code, you simply get a silent change of the value. The debug code will contain traps for this, but these disappear in release; and of course optimised code is the most vulnerable. Live fast die young. And so we get a “Heisenbug”, an error that changes when you try to observe it in the debugger. This could still happen if the “constant” string was #defined, because the value has to be somewhere, even if the compiler has folded long strings.

Now, modern operating systems allow you to write-protect blocks of memory; but that is both non-portable and deeply ugly - and you’ve probably worked out for yourself that it can be turned off by the same API. In older compilers, it is also entirely possible for explicit constants to be changed; and Microsoft actually used to advise developers to write code such as:

char* Fname = " ImpliedVol"; // Note the space at the start of the name
Fname[0] = (BYTE) strlen(Fname);

to cope with BASIC-style APIs that required the length of the string to be in the first byte. It worked - yes, really; a large percentage of the world’s derivative pricing makes its way into dealing systems that way - but Microsoft now makes all literal strings read only, which is more elegant. Although it does mean that, a whole pile of its example code now won’t work anymore.

And, of course, any memory value can be hit by a stray pointer.

However, it’s not all bad as const can also be a big hint to the compiler to make better code. If you have a big vector of some class, then default pass by value means that a simple utility algorithm like:

template <typename T> double Sum(vector<T> vec)

may end up calling the constructor many times. As it happens, STL is smart enough to keep this to a minimum; but if we write:

template <typename T> double Sum(const vector<T>& vec)

then we are telling the compiler quite explicitly that vec is basically just an alias to the variable in the calling function; and then it’s much easier for the compiler to generate direct inline access. Thus, const references can make your code both faster and more robust.

So, to summarise, C++ does do constants, and often they are useful, but they are never completely trustworthy. ®

The essential guide to IT transformation

More from The Register

next story
Munich considers dumping Linux for ... GULP ... Windows!
Give a penguinista a hug, the Outlook's not good for open source's poster child
The Return of BSOD: Does ANYONE trust Microsoft patches?
Sysadmins, you're either fighting fires or seen as incompetents now
Intel's Raspberry Pi rival Galileo can now run Windows
Behold the Internet of Things. Wintel Things
Microsoft cries UNINSTALL in the wake of Blue Screens of Death™
Cache crash causes contained choloric calamity
Eat up Martha! Microsoft slings handwriting recog into OneNote on Android
Freehand input on non-Windows kit for the first time
Linux kernel devs made to finger their dongles before contributing code
Two-factor auth enabled for Kernel.org repositories
Time to move away from Windows 7 ... whoa, whoa, who said anything about Windows 8?
Start migrating now to avoid another XPocalypse – Gartner
prev story

Whitepapers

5 things you didn’t know about cloud backup
IT departments are embracing cloud backup, but there’s a lot you need to know before choosing a service provider. Learn all the critical things you need to know.
Implementing global e-invoicing with guaranteed legal certainty
Explaining the role local tax compliance plays in successful supply chain management and e-business and how leading global brands are addressing this.
Build a business case: developing custom apps
Learn how to maximize the value of custom applications by accelerating and simplifying their development.
Rethinking backup and recovery in the modern data center
Combining intelligence, operational analytics, and automation to enable efficient, data-driven IT organizations using the HP ABR approach.
Next gen security for virtualised datacentres
Legacy security solutions are inefficient due to the architectural differences between physical and virtual environments.