Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C memset seems to not write to every member

I wrote a small coordinate class to handle both int and float coordinates.

template <class T>
class vector2
{
public:
    vector2() { memset(this, 0, sizeof(this)); }
    T x;
    T y;
};

Then in main() I do:

vector2<int> v;

But according to my MSVC debugger, only the x value is set to 0, the y value is untouched. Ive never used sizeof() in a template class before, could that be whats causing the trouble?

like image 400
Mizipzor Avatar asked Apr 12 '09 20:04

Mizipzor


People also ask

What is Memset in C with example?

C library function - memset() Description. The C library function void *memset(void *str, int c, size_t n) copies the character c (an unsigned char) to the first n characters of the string pointed to, by the argument str. Declaration. Following is the declaration for memset() function.

What is the difference between Memset () and STR[]?

If n is greater than the size of the object pointed to by str, the behavior is undefined. str [] : Pointer to the object to copy the character. ch : The character to copy. n : Number of bytes to copy. Return value: The memset () function returns str, the pointer to the destination string.

Should Memset_s pull in fprintf from standard library?

Here you have a memset_s function, whose job is to set a range of bytes to a value... and you've got it pulling in fprintf from the standard library! Does that seem appropriate to you? memset_s is a very low-level function. It should be usable even on embedded systems that have never heard of fprintf or stderr.

What is the return value of Memset?

Return value: The memset () function returns str, the pointer to the destination string. Note: We can use memset () to set all values as 0 or -1 for integral data types also. It will not work if we use it to set as other values.


2 Answers

No don't use memset -- it zeroes out the size of a pointer (4 bytes on my x86 Intel machine) bytes starting at the location pointed by this. This is a bad habit: you will also zero out virtual pointers and pointers to virtual bases when using memset with a complex class. Instead do:

template <class T>
class vector2
{
public:
    // use initializer lists
    vector2() : x(0), y(0) {}
    T x;
    T y;
};
like image 68
dirkgently Avatar answered Sep 22 '22 06:09

dirkgently


As others are saying, memset() is not the right way to do this. There are some subtleties, however, about why not.

First, your attempt to use memset() is only clearing sizeof(void *) bytes. For your sample case, that apparently is coincidentally the bytes occupied by the x member.

The simple fix would be to write memset(this, 0, sizeof(*this)), which in this case would set both x and y.

However, if your vector2 class has any virtual methods and the usual mechanism is used to represent them by your compiler, then that memset will destroy the vtable and break the instance by setting the vtable pointer to NULL. Which is bad.

Another problem is that if the type T requires some constructor action more complex than just settings its bits to 0, then the constructors for the members are not called, but their effect is ruined by overwriting the content of the members with memset().

The only correct action is to write your default constructor as

vector2(): x(0), y(0), {}

and to just forget about trying to use memset() for this at all.

Edit: D.Shawley pointed out in a comment that the default constructors for x and y were actually called before the memset() in the original code as presented. While technically true, calling memset() overwrites the members, which is at best really, really bad form, and at worst invokes the demons of Undefined Behavior.

As written, the vector2 class is POD, as long as the type T is also plain old data as would be the case if T were int or float.

However, all it would take is for T to be some sort of bignum value class to cause problems that could be really hard to diagnose. If you were lucky, they would manifest early through access violations from dereferencing the NULL pointers created by memset(). But Lady Luck is a fickle mistress, and the more likely outcome is that some memory is leaked, and the application gets "shaky". Or more likely, "shakier".

The OP asked in a comment on another answer "...Isn't there a way to make memset work?"

The answer there is simply, "No."

Having chosen the C++ language, and chosen to take full advantage of templates, you have to pay for those advantages by using the language correctly. It simply isn't correct to bypass the constructor (in the general case). While there are circumstances under which it is legal, safe, and sensible to call memset() in a C++ program, this just isn't one of them.

like image 26
RBerteig Avatar answered Sep 21 '22 06:09

RBerteig