Reputation: 28837
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?
Upvotes: 11
Views: 1728
Reputation: 385144
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.
Upvotes: 2
Reputation: 791729
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.
Upvotes: 14
Reputation: 320421
With the exception of run-time sized cases, the typical idiomatic way to use memset
(as well as memcpy
, malloc
, etc.) is to do
memset(dst_ptr, 0, sizeof *dst_ptr);
or, equivalently,
memset(&dst_object, 0, sizeof dst_object);
Which is how it should have been used in this case as well
memset(&fred, 0, sizeof fred);
and the problem with the name conflict would not arise. The memset(fred, 0, sizeof fred)
variant will work as well.
Upvotes: 0
Reputation: 490108
When you define the variable, it hides the name of the type, so yes, when you do sizeof(fred)
, you're getting the size of the array, not the size of the struct. it's pretty easy to verify this by simply printing out sizeof(fred)
.
The short answer, however, is just: "don't do that."
Upvotes: 1
Reputation: 1686
Shouldn't this be sizeof(fred)*(MAX_SIZE+1) since your array is MAX_SIZE+1 long?
Upvotes: 2