-2

Because of differences for zero initialization between compilers when using () {} I prefer memset. How safe is this code ?

class A {
public:
  int x;
  double y[55];
  bool a, b;
  A() {
    memset(&x, 0, int(&b)+sizeof(bool)-int(&x));// cannot use sizeof(A) but I know address of first and last
  }
};

I do not have user-defined types, classes, structs from x variable to b variable. I know the address of the first variable and the last. Can I be sure that all the variables defined between x and b, including those in array are zero in all MSVC and Mac compilers after 2010 ?

_____ EDITED _____

Who consider this duplicate, please show me where in the "original" is this solution: memset(&x, 0, int(&b)+sizeof(bool)-int(&x)); ???

This is the key for using memset(), not professional at all to close the thread like this !!!

In meantime I find a more good solution: memset(&x, 0, size_t(&b)+sizeof(bool)-size_t(&x));

Caty
  • 63
  • 6
  • 3
    What's wrong with an explicit default constructor using initialization list? – Thomas Matthews May 15 '23 at 15:14
  • 4
    Just use this (default member initializers): `int x{}; std::array y{}; bool a{}; bool b{};`. memset should not have to occur in current C++ at all. (std::fill). Or indeed just use initializer lists. – Pepijn Kramer May 15 '23 at 15:14
  • 1
    Why can't you use `sizeof(A)`? What difference are you referring to with `{}` between compilers? Have you considered default member initializers like `int x = 0;` and `double 7[55] = {};`? – François Andrieux May 15 '23 at 15:15
  • 5
    Your memset isn't safe at all. You should not make assumptions on how members are organized in memory (padding, alignment etc). – Pepijn Kramer May 15 '23 at 15:16
  • 2
    Side note: if you're going to cast pointers to integers, use `intptr_t` instead of regular old `int`. `intptr_t` is guaranteed to be large enough to hold an address. `int` is not. – user4581301 May 15 '23 at 15:19
  • Need maximum compatibility, x{} is not available on MSVC 2010. If I use x(), y() on the class where I use this class do not make zero initialization on MSVC 2015 - how hard is to make a zero initialization ... – Caty May 15 '23 at 15:19
  • 4
    @Caty Then do it "the right way": `A() : x(), y(), a(), b() {}`. `T()` has produced a value initialized (zero for fundamental types) since C++03 – NathanOliver May 15 '23 at 15:24
  • Then just be explicit and initialize all members one by one. – Pepijn Kramer May 15 '23 at 15:24
  • *"cannot use sizeof(A)"* Why not? All the major compilers accept (see https://godbolt.org/z/bE3aqqh8x ) it and even if they didn't they didn't, you should be using [`offsetof`](https://en.cppreference.com/w/cpp/types/offsetof) here... – fabian May 15 '23 at 15:34
  • I'm pretty sure MSVC 2010 does support `x{}` syntax, as it supports most of C++11. – Mooing Duck May 15 '23 at 15:44
  • 1
    @MooingDuck -- Unfortunately, there are some aspects of the `{}` that are broken in MSVC 2010. I had to work with that compiler a few years ago, and found out that the brace-initializer syntax was not implemented fully. My experience is that the minimum is VS 2015, SP3 for C++11 compliance. – PaulMcKenzie May 15 '23 at 15:52
  • @PaulMcKenzie: I don't have an MSVC 2010 handy to test, but I'm pretty sure that `A() : x{}, y{}, a{}, b{} ()` worked fine at least – Mooing Duck May 15 '23 at 16:23
  • *How safe is this code?* Ignoring the undefined behavior, and ignoring the fragility of future changes that may add or rearrange fields thus making the byte size calculation incorrect, and ignoring future changes that may make the class not *is trivially copyable*, it seems fine. – Eljay May 15 '23 at 16:34
  • You seem to be confused about what the C++ language guarantees and what the current state of modern compilers is. 1) learn the language) 2) zero initialize according to the standard 3) Stop using ancient compilers. – Jesper Juhl May 16 '23 at 01:48

1 Answers1

4

Because of differences for zero initialization between compilers when using () {}

There is no difference. It is standardized and hasn't been changed significantly since C++03 in the case of () and since C++11 with the addition of {}. In particular both () and {} would have the same effect if your class didn't have a constructor and both would zero-initialize. If your class does have a user-declared/provided constructor, then the class isn't aggregate and so neither () nor {} will cause zero-initialization.

The memset on the other hand has implementation-defined behavior at best. In particular int(&b) is only allowed if int is large enough to hold a pointer value (i.e. it will fail on common 64bit systems). If it does work it has an implementation-defined value and everything you do with it is also implementation-defined.

Whether memset on a pointer to x instead of the A object is allowed at all, is another issue of concern.

If A wasn't trivially-copyable, then memset would have undefined behavior regardless.


The correct way to zero-initialize all members of a class explicitly with a non-aggregate class, i.e. one that has a user-declared/provided constructor, is to use the member initializer list to individually value-initialize the members, since C++03 like this:

  A() : x(), y(), a(), b() { /* do constructor things */ }

(With C++11 or later {} default member initializers may be used instead as well.)

This initializes everything to zero, except padding. If you really need that for some reason and your class is trivially-copyable and *this isn't potentially overlapping (i.e. it isn't used as a base class subobject or a member subobject with [[no_unique_address]]), then a correct memset use would be:

memset(this, 0, sizeof(*this));

However, note that memseting all bytes to zero technically also doesn't imply that the members will be set to value zero. It is valid for an implementation to use a different bit pattern than all-zero to represent the zero or null pointer value of a scalar type.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • Sure ? Try MSVC 2015 please and try to use () and see the result. And if I want to use an array of A a[55] how to zero initialize this ? – Caty May 15 '23 at 15:29
  • 1
    @Caty `A a[55]{};` will do it if `A` is an aggregate. – Ted Lyngmo May 15 '23 at 15:30
  • @Caty I don't have MSVC 2015 available to test with it. If it indeed doesn't zero-initialize when you use `()`, then it is non-conforming. I have added a second part to my answer explaining how to properly zero the object explicitly. – user17732522 May 15 '23 at 15:34
  • @Caty Arrays (of scalar types) can also be zero-initialized with both `()` and `{}`. – user17732522 May 15 '23 at 15:34
  • @Caty The earliest MSVC I can use on compiler explorer is Visual Studio 2017's and it correctly zeros with `()` if the class is aggregate. In fact it translates to the correct memset. https://godbolt.org/z/zGzW4WWe3 – user17732522 May 15 '23 at 15:42
  • 1
    @Caty -- I just tested with Visual C++ 2015, and there is no issues with the initialization. How are you verifying that the `()` does not work with VS 2015? Also, I used Service Pack 3, as that is the official version that actually implements C++11 as fully as possible. Before SP3, there were some language conformance issues (for example, static variables within functions not being thread-safe), so unless you are using something before SP3, I saw no issues. – PaulMcKenzie May 15 '23 at 15:48
  • How I say, please read my post: need to work in compilers from 2010 . – Caty May 15 '23 at 16:12
  • I used other class with object of A. B(), a() { //x=1453975435; } – Caty May 15 '23 at 16:13
  • Still cannot have in test any problems with memset. If memory padding, alignment what, in my code is not used sizeof(A). Using typedef unsigned long long int uint; solve the problem of (int) zise – Caty May 15 '23 at 16:16
  • The problem with memset(this, 0, sizeof(*this)); is if I use some user defined types, structs etc – Caty May 15 '23 at 16:18
  • 1
    @Caty Sorry, but I have no idea what you are talking about. Can you provide a concrete example in your question or via a godbolt link for which your compiler does not zero-initialize where you expect it or where `sizeof(A)` or `memset(this, 0, sizeof(*this));` or `A() : x(), y(), a(), b()` doesn't work? – user17732522 May 15 '23 at 16:25
  • 3
    @Caty -- Again, *how* are you verifying your results? Are you using the debugger? Are you running a release version of your application? Did you write a simple program that actually `std::cout`'s those values from the instance of `A`? If you are running a release version and using the debugger, please be aware that the compiler's optimizer does *not* have to initialize anything if it detects you are not accessing those member variables. – PaulMcKenzie May 15 '23 at 16:35