Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Someone is using the struct name as a variable name too. What does the code really say?

This morning we found an old chunk of code that was causing a library call to crash.

struct   fred
{
    int     a;
    int     b;
    int     c;
};

fred     fred[MAX_SIZE+1];

memset( fred, 0, sizeof(fred) * MAX_SIZE+1 );

It appears that the sizeof(fred) may have been the full array size, rather than the structure size, as it was overwriting a great deal of memory.

The fact that it compiled without warning on several different systems seemed odd.

Is there a correct semantic for this case where the type and variable name are colliding? or is this some sort of undefined behavior? or just a defect?

like image 886
EvilTeach Avatar asked Apr 20 '10 17:04

EvilTeach


3 Answers

Number one would be, don't do this as it's confusing - but you've already discovered this.

The variable hides the name of the struct, but you can still use struct fred to refer to the type.

e.g.

fred     fred[MAX_SIZE+1];

memset( fred, 0, sizeof(struct fred) * (MAX_SIZE+1) );

Alternatively, why not just use the size of the complete object. That way your memset call is robust in the face of changes to either the array size or type. You can do:

memset( fred, 0, sizeof fred );

You must have the parentheses when using a type id with sizeof but it's not needed when you use an object.

like image 159
CB Bailey Avatar answered Oct 19 '22 17:10

CB Bailey


Shouldn't this be sizeof(fred)*(MAX_SIZE+1) since your array is MAX_SIZE+1 long?

like image 21
martiert Avatar answered Oct 19 '22 17:10

martiert


The latest declaration takes precedence:

[C++03: 9.1/2]: A class definition introduces the class name into the scope where it is defined and hides any class, object, function, or other declaration of that name in an enclosing scope (3.3). If a class name is declared in a scope where an object, function, or enumerator of the same name is also declared, then when both declarations are in scope, the class can be referred to only using an elaborated-type-specifier (3.4.4).

An elaborated-type-specifier is when you stick struct or class at the front of the type; this effectively disambiguates it, though, strictly speaking, and due to the above cited rule, the lookup was never really ambiguous in the first place.

So:

void foo()
{
   struct bar {};
   bar bar[5];

   memset(bar, 0, sizeof(bar));
   //             ^^^^^^^^^^^
   //                  5

   memset(bar, 0, sizeof(struct bar));
   //             ^^^^^^^^^^^^^^^^^^
   //                  1
}

// (NB. Exact sizes may differ; 1 and 5 given as relative examples only)

The fact that this is all well-defined is one reason that you didn't get a warning. Still, I'd hope that an intelligent compiler would spot your code as a possible programmer mistake — rationalising about why some given implementation does or does not emit some given warning in some non-mandated case, though, is largely folly.

like image 35
Lightness Races in Orbit Avatar answered Oct 19 '22 17:10

Lightness Races in Orbit