37

For this simplified test case:

#include <map>

class Tester {
    int foo;
    std::map<int, int> smap;
};

int main() {
    Tester test; 
    return 0;
}

I get the following compiler warning:

$ clang++ -std=c++98 -Weverything test.cc 
test.cc:5:24: warning: padding class 'Tester' with 4 bytes to align 'smap' [-Wpadded]
    std::map<int, int> smap;
                       ^

Can anyone explain what this warning means, and how I should address it?

vitaut
  • 49,672
  • 25
  • 199
  • 336
Dun Peal
  • 16,679
  • 11
  • 33
  • 46
  • 40
    It means you've turned on too many warnings. :) – Retired Ninja Nov 25 '13 at 03:32
  • Yeah, struct padding is a *feature*, not something to be warned about. I suppose the flag is there so that people who need to know the *exact* layout of their structs in memory can add explicit padding and be warned whenever they missed some, but that's not a normal scenario at all. – hobbs Nov 25 '13 at 03:39
  • 1
    Yes, padding is a feature. This is _not_ about explicit member layout. This is about letting the user know that their class is not making efficient use of memory, which can (and does, in some cases) result in serious performance hits thanks to how the memory bus and CPU caches work. A good programmer working in a low-level language should be aware of padding and minimize it. You can often trivially rearrange a class/struct to avoid it and this warning lets you know when those cases occur. – Sean Middleditch Nov 25 '13 at 07:52

3 Answers3

30

There's no real problem here. In C and C++, the compiler is allowed to insert padding after struct members to provide better alignment, and thus allow faster memory access. In this case, it looks like has decided to place smap on an 8-byte alignment. Since an int is almost certainly four bytes, the warning is telling you that there are four bytes of wasted space in the middle of the struct.

If there were more members of the struct, then one thing you could try would be to switch the order of the definitions. For example, if your Tester had members:

 struct Tester {
     int foo;
     std::map<int, int> smap;
     int bar;
 };

then it would make sense to place the two ints next to each other to optimise alignment and avoid wasted space. However, in this case, you only have two members, and if you switch them around then the compiler will probably still add four bytes of padding to the end of the struct in order to optimise the alignment of Testers when placed inside an array.

Tristan Brindle
  • 16,281
  • 4
  • 39
  • 82
  • 1
    Thanks! Putting `foo` after `smap` emits a different warning: `padding size of 'Tester' with 4 bytes to alignment boundary`, probably to signify that those those 4 padding bytes are inserted before the end of the class, rather than before `smap`. I can also eliminate the warning by converting `foo` into a `long`. The only question is: why don't I get the warning when `int foo` is the only variable in `Tester`? – Dun Peal Nov 25 '13 at 03:44
  • @DunPeal Because integers only require 4-byte alignment, so it doesn't need to add any padding at the end of the struct in order to optimally place them in an array. If you try changing `foo` to `short` or a `bool`, you might see the warning pop up again (then again, you might not, I'm not sure). – Tristan Brindle Nov 25 '13 at 03:46
  • 1
    From the answers to this question, I thought the compiler on my platform (LP64) always strives to make sure every struct is allocated a multiple of 64? Otherwise, why bother padding when `foo` is last member? – Dun Peal Nov 25 '13 at 03:57
  • 1
    @DunPeal The padding after the last member is so that when you create an array of objects, the first member of each element is properly aligned. So for example, if the sizes of the things inside `Tester` added up to 12 bytes, you'd probably expect the compiler to add four bytes of padding at the end, so that each element in a `Tester[]` array was aligned to a 16-byte boundary. Alignments don't necessarily have to be multiples of 64 bits (=8 bytes) though, 4 byte alignment is okay for ints, for example. – Tristan Brindle Nov 25 '13 at 08:01
  • 3
    you can ignore it in code like: `#pragma clang diagnostic push` `#pragma clang diagnostic ignored "-Wpadded"` `volatile bool connected = true;` `#pragma clang diagnostic pop` – leanid.chaika Jun 10 '15 at 20:55
7

I'm assuming you're compiling this on a 64-bit system.

On 64-bit systems, pointers are 8 bytes. Compilers will align structure members to natural boundaries, so an 8-byte pointer will start at an offset in a structure that is a multiple of 8 bytes.

Since int is only four bytes, the compiler inserted 4 bytes of "padding" after foo, so that smap is on an 8-byte boundary.

Edit: While smap is not a pointer, but a std::map, the same logic applies. I'm not sure what the exact rules for alignment of objects are, but the same thing is happening.

What to do? Nothing. Your code is perfectly fine, the compiler is just letting you know that this has taken place. There's absolutely nothing to worry about. -Weverything means turn on every possible warning, which is probably excessive for most all compilations.

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • Thanks! Seems like you are right; when I change `foo` to `long`, the warnings disappears. From you answer I understand that the only effective meaning of this warning is that 4 bytes of memory will be wasted for every instance of `Tester`. – Dun Peal Nov 25 '13 at 03:38
  • To access the `smap` member, it would still be more expensive to access it with a half-pointer-size offset (4 bytes) than full-pointer size (8 bytes), because we're talking from the beginning of the struct. – bobobobo Nov 25 '13 at 03:51
  • 4
    @DunPeal It's not just that the memory will be _wasted_, it's also that if you binary-serialize this data structure, then unserialize it, say on a 32-bit machine, you're going to have this mysterious padding in there that you might not have known the compiler inserted. While you wouldn't binary-serialize a `std::map`, still. If this were some pack of primitives that would loan themselves to easy binary serialization, you might have done it. The compiler wants you to know that `smap` will be 4 bytes further ahead of where you think you put it. – bobobobo Nov 25 '13 at 03:52
  • Oh `$deity`, please do not try and binary serialize something with, for example, a `std::map` embedded in it. – Jonathon Reinhart Nov 25 '13 at 03:54
0

Your compiler on your sytsem chose to give pointers on your 64bit system 8 bytes, int in the struct has 4 bytes. Similar problems/warnings are occurring to me a lot those days working with older code examples so I had to dig deeper.

To make it short, int was defined in the 60's with no 64 bit system, no Gigabytes of storage nor GB of ram in mind.

To solve your error message use size_t (size type) instead of int when necessary - in your case with the map stl since it is programmed to run on multiple different systems.

With size_t your compiler can choose itself what byte size it needs if it compiles on a 32 bit system or a 64 bit system or arm or what ever and the message is gone and you won't even have to modify your code no matter what system you may compile your code for in the future.

Ingo Mi
  • 999
  • 12
  • 26
  • The compiler is choosing the size of int too. Int is not hard defined and depends on the system architecture. See https://en.cppreference.com/w/cpp/language/types#Properties. – Marc Feb 13 '23 at 12:54