20

memset is sometimes used to initialize data in a constructor like the example below. Does it work in general ? Is it a good idea in general?

class A {
public:
   A();
private:
   int a;
   float f;
   char str[35];
   long *lp;
};

A::A()
{
   memset(this, 0, sizeof(*this));
}
skydoor
  • 25,218
  • 52
  • 147
  • 201

2 Answers2

27

Don't use memset. It's a holdover from C and won't work on non-PODs. Specifically, using it on a derived class that contains any virtual functions -- or any class containing a non-builtin -- will result in disaster.

C++ provides a specific syntax for initialization:

class A {
public:
   A();
private:
   int a;
   float f;
   char str[35];
   long *lp;
};

A::A()
    : a(0), f(0), str(), lp(NULL)
{
}

To be honest, I'm not sure, but memset might also be a bad idea on floating-points since their format is unspecified.

rlbond
  • 65,341
  • 56
  • 178
  • 228
  • 1
    There's also `std::fill` or `std::fill_n` if you need to "memset" an array (or any other sequence) – jalf Mar 20 '10 at 02:53
  • For the array case it's better to do `str()`. GCC has problems initializing the char array by a string literal through a member-initializer, and if the array is not a char array it won't work even in a standard conforming compiler. `str()` will always nullify the array. (i just submitted a bug report: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43453 ) – Johannes Schaub - litb Mar 20 '10 at 03:07
  • @litb: Noted and changed above. – rlbond Mar 20 '10 at 16:16
  • For C++11 I would add `= {}` for each field definition https://godbolt.org/z/91Wfajrjn – Marek R Jul 04 '23 at 12:03
17

It's a terrible idea. You're just tromping over data, paying no heed to how objects should be initialized. If your class is virtual, you're likely to wipe out the vtable pointer as well.

memset works on raw data, but C++ isn't about raw data. C++ creates abstractions, so if you want to be safe you use those abstractions. Use the initializer list to initialize members.

You can do it to POD types:

struct nothing_fancy_here
{
    bool b;
    int i;
    void* p;
};

nothing_fancy_here x;
memset(&x, 0, sizeof(x));

But if you're doing it on this, that means you're in a user-defined constructor and no longer qualify as a POD type. (Though if all your members are POD it might work, as long as none contain 0 as a trap value. I'm sure not sure if any other sources of undefined behavior come into play here.)

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • 1
    Doesn't your `nothing_fancy_here` type also have a constructor? I don't think there's any difference between what you are calling a POD type, and the OPs example class... Whether you are calling `memset` from within the constructor or not is irrelevant. If it's ok in your example, it's ok in the OP's. –  Mar 20 '10 at 02:34
  • @STingRay: It has no constructor. Maybe the `// ...` is too open. – GManNickG Mar 20 '10 at 02:39
  • It does have a constructor. Granted it's a no-op. The point about whether there is an explicitly-declared constructor or not is irrelevant. What matters is, as you said, whether the struct/class (or any of its members' types) has virtual member functions. If the OP hadn't declared a constructor and called `memset` just as you did, there is no difference. So the ultimate answer is: in the given examples, it is technically *ok* to do, but as a general practice, certainly not... –  Mar 20 '10 at 02:47
  • 2
    @STingRay: Sorry, I meant user-defined constructor. Having a user-defined constructor, like the OP has, means it's not an aggregate and therefore not a POD. Note I'm calling `memset` outside the class. Calling it in the constructor automatically means it's not a POD type, because I've defined a constructor. – GManNickG Mar 20 '10 at 02:55
  • `` Thanks for keeping that other question alive. It's a bit more complicated than I had hoped. –  Mar 20 '10 at 02:56
  • @GMan: I still argue that whether or not a user-defined constructor exists is irrelevant. Why does having a user-defined constructor pose risk in using the `memset` construct? I believe the only valid concern is overwriting the vtable pointer, but even that must depend on implementation-defined behavior (depending on exactly when the vtable pointer is initialized). Of course the same concern applies recursively to member data of user-defined types... –  Mar 20 '10 at 03:17
  • @STingRay: I agree it's a bit gray. The thing is, it *is* undefined behavior to call `memset` on a non-POD, I think we agree. So if I do it on a `std::string` be it a member or not, the result is undefined. If we have a struct that contains non-POD members and we `memset` that struct, we have undefined behavior. Now consider a class with only POD members, but is not an aggregate (has a user defined constructor). *By definition*, it is not a POD now, so even though all of it's members are POD, it itself is not so we cannot have a well-defined `memset`. Considering that, it makes no difference.. – GManNickG Mar 20 '10 at 03:25
  • ..if that `memset` call was external from the class or internal (like the constructor). The point remains, with a constructor it's not POD and cannot be safely `memset`'d. Practically, it'll probably be fine, but technically speaking it's not. – GManNickG Mar 20 '10 at 03:26
  • @GMan: So, the standard explicitly states that using `memset` to initialize the memory occupied by a non-POD type is UB? (My copy of the standard is in the mail.) –  Mar 20 '10 at 04:15
  • @STingRaySC: No, there are dots to connect, but they are there. That said, I can't find them right now. :P However, the strings library section (where `memset` is defined) starts with: "This clause describes components for manipulating sequences of "characters,” where characters may be of any POD (3.9) type." It pretty much comes down to: non-POD cannot be treated as a contiguous sequence of bits. – GManNickG Mar 20 '10 at 04:49
  • GManNickG you find a compiler which return different result between sizeof(x) and sizeof(*this) ? – Caty May 16 '23 at 11:54