[ Team LiB ] |
![]() ![]() |
Gotcha #21: Self-InitializationWhat is the value of the inner var in the following code?
int var = 12;
{
double var = var;
// . . .
It's undefined. In C++, a name comes into scope before its initializer is parsed, so any reference to the name within the initializer refers to the name being declared! Not many programmers will compose as strange a declaration as the one above, but it's possible to cut and paste your way into trouble:
int copy = 12; // some deeply buried variable
// . . .
int y = (3*x+2*copy+5)/z; // cut this . . .
// . . .
void f() {
// need a copy of y's initial value . . .
int copy = (3*x+2*copy+5)/z; // and paste it here!
// . . .
Use of the preprocessor can produce essentially the same error as indiscriminate cutting and pasting (see Gotcha #26): int copy = 12; #define Expr ((3*x+2*copy+5)/z) // . . . void g() { int copy = Expr; // déjà vu all over again . . . // . . . Other manifestations of this problem occur when naming conventions fail to distinguish adequately between type names and non-type names:
struct buf {
char a, b, c, d;
};
// . . .
void aFunc() {
char *buf = new char[ sizeof( buf ) ];
// . . .
The local buf will (probably) refer to a 4-byte buffer, big enough to hold a char *. This error could go undetected for a long time, especially if a struct buf happens to be the same size as a pointer. Following a naming convention that distinguishes type names from non-type names would have circumvented this problem (see Gotcha #12): struct Buf { char a, b, c, d; }; // . . . void aFunc() { char *buf = new char[ sizeof( Buf ) ]; // OK // . . . OK, so we now know to avoid the canonical manifestation of this gotcha:
int var = 12;
{
double var = var;
// . . .
How about a variation on this theme? const int val = 12; { enum { val = val }; // . . . What's the value of the enumerator val? Undefined? Guess again. The value is 12, and the reason is that the point of declaration for the enumerator val is, unlike that of a variable, after its initializer (or, more formally, after its enumerator-definition). The val after the = symbol in the enum refers to the outer-scope const. This could lead to discussion of an even more involved situation:
const int val = val;
{
enum { val = val };
// . . .
Thankfully, this enumerator-definition is illegal. The initializer is not an integer constant-expression, because the compiler can't determine the value of the outer-scope val at compile time. |
[ Team LiB ] |
![]() ![]() |