Main Content

Unnecessary padding

Members of a struct are padded to fulfill alignment requirement when rearranging the members to fulfill this requirement saves memory

Since R2021b

Description

This checker flags a struct object where the arrangement of its members necessitates additional padding to fulfill alignment requirement. Rearranging the members of such a struct object might fulfill the alignment requirement without requiring any additional padding. Because the padding is unnecessary for alignment purposes, eliminating the padding saves memory. Consider this struct in a 64bit system:

 struct A {
      uint32_t m1;// 4 bytes
      uint64_t m2;// 8 bytes
      uint32_t m3;// 4 bytes
  }; 
 
To maximize speed, C/C++ requires that a variable be read in one cycle if possible. In this system, 8 bytes can be read during one cycle. If m1 and m2 are placed consecutively, the machine requires two cycles to read m2. Instead, the variable m1 is placed in a 8 byte slot by itself after padding it by 4 bytes. Then m2 is placed in its own 8 byte slot. The variable m3 is also padded to fulfill alignment requirement for the struct A. Because of the padding, the size of A is 24 bytes even though the combined size of m1, m2, and m3 is 16.

Polyspace® raises this defect when alignment requirement can be fulfilled by rearranging the members of a struct. For instance, rearranging the members of A can eliminate padding:

 struct A {
      uint64_t m2;// 8 bytes
      uint32_t m1;// 4 bytes
      uint32_t m3;// 4 bytes
  }; 
 
Here, m2 is placed first in a 8 byte slot. Then m1 and m3 are placed together in another 8 byte slot. This rearrangement eliminates the padding.

Risk

Unnecessary padding wastes memory, which can have several adverse impacts:

  • Using more memory than necessary might exhaust the available memory, resulting in paging fault.

  • Functions such as memcpy and memcmp might take longer.

Fix

To fix this defect, rearrange the members of the struct to eliminate the unnecessary padding. Declare the largest struct members first, and then keep the declarations of same-sized members together. You might also use pragma directives to eliminate padding.

Performance improvements might vary based on the compiler, library implementation, and environment that you are using.

Examples

expand all

#include <stdint.h>
struct GlobalStructA {  
    uint32_t m1;                
    uint64_t m2;
    uint32_t m3;                
};

// Using pragma pack to eliminate padding
//
#pragma pack(push, 1)
struct GlobalStructPragmaPack {          
    uint8_t  m1;
    uint64_t m2;
    uint8_t  m3;
};
#pragma pack(pop)
struct Array                   
{
    uint8_t  m1[5];
    uint64_t m2;
    uint8_t  m3[3];
};
struct StructWithBitField    
{
    uint8_t  m1 : 1;
    uint8_t     : 1;
    uint8_t     : 1;
    uint8_t  m2 : 1;

    uint64_t m3;

    uint8_t  m4 : 1;
    uint8_t     : 1;
    uint8_t     : 1;
    uint8_t  m5 : 1;
};

In this example, Polyspace flags C style struct objects that contain unnecessary padding. Polyspace assumes that the processor has a 32-bit word length by default. Use the option -target x86_64 to run this example.

  • Because the 32-bit variable m1 is declared first in the GlobalStructA object, it is padded to 64 bit. Polyspace flags the object because the padding can be eliminated by declaring m1 after m2.

  • The object GlobalStructPragmaPack has a similar issue with the order of its member declaration, but the padding is eliminated by a pragma directive. Polyspace does not flag this object.

  • In the object Array, because m1[5] is declared before m2, the 40-bit array is padded to 64 bit. The 24-bit array m3 is padded to 64 bit. These paddings can be eliminated by declaring m2 first. Because Array contains unnecessary padding, Polyspace flags the object.

  • In the object StructWithBitField, there are two bit-fields, each consisting of 4 bits. Because m3 is declared between these two bitfields, they are padded. Polyspace flags the object.

Correction

To fix this defect, change the order of the declaration to eliminate padding. For instance:

  

#include <stdint.h>
struct GlobalStructA {          
	uint64_t m2;
	uint32_t m1;                
	uint32_t m3;                
};

// Using pragma pack to eliminate padding
//
#pragma pack(push, 1)
struct GlobalStructPragmaPack {           
	uint64_t m2;
	uint8_t  m1;
	uint8_t  m3;
};
#pragma pack(pop)
struct Array                   
{
	uint64_t m2;
	uint8_t  m1[5];
	uint8_t  m3[3];
};
struct StructWithBitField    
{
	uint64_t m3;
	
	uint8_t  m1 : 1;
	uint8_t     : 1;
	uint8_t     : 1;
	uint8_t  m2 : 1;

	uint8_t  m4 : 1;
	uint8_t     : 1;
	uint8_t     : 1;
	uint8_t  m5 : 1;
};
#include <string>
#include<stdint.h>
class GlobalClassWithString {
    uint32_t    m1;
    std::string m2;
    uint32_t    m3;
};
class GlobalClassC {
  public:
    uint32_t m1;
  protected:
    uint64_t m2;
  private:
    uint32_t m3;
};

Polyspace assumes that the processor has a 32-bit word length by default. Use the option -target x86_64 to run this example. Because of the order in which the members of the preceding classes are declared, the classes contain unnecessary padding. Polyspace flags these classes. The public, private, or protected labels of the members do not impact how they are organized in the memory.

Correction

To fix this defect, change the order of the declaration to eliminate padding. For instance:

#include <string>
#include<stdint.h>
class GlobalClassWithString{
    std::string m2;
	uint32_t    m1;
    uint32_t    m3;
};
class GlobalClassC {
  protected:
    uint64_t m2;  
  public:
    uint32_t m1;
  private:
    uint32_t m3;
};
#include <stdint.h>
struct SmallPadding {     //Defect in 16-bit, no defect in 32-bit
    uint8_t m1;
    uint8_t m2;
    uint8_t m3;
    uint8_t m4;
    uint8_t m5;
    uint8_t m6;
    uint8_t m7;
    uint32_t m8;
    uint8_t m9;
    uint8_t m10;
    uint8_t m11;
    uint8_t m12;
    uint8_t m13;
    uint8_t m14;
    uint8_t m15;
};

This example shows the sensitivity of this checker to the processor word length. In the object SmallPadding, there are 16 bits of padding, which is small compared to the total size of the struct. When the processor word length is large, the small padding is unlikely to cause inefficiency. For instance, reading SmallPadding requires three cycles regardless of the padding if the processor has a 64-bit alignment. Polyspace does not flag the object in this case. When the processor word length is smaller, the padding can lead to inefficiency. For instance, if the processor alignment is 16 bit, the added padding might cause inefficiency in reading the object. Polyspace flags SmallPadding in this case. To set the alignment of processor to 16 bit in a Polyspace analysis, use the options -target mcpu -align 16. See Generic target options.

Result Information

Group: Performance
Language: C | C++
Default: Off
Command-Line Syntax: UNNECESSARY_STRUCT_PADDING
Impact: Medium

Version History

Introduced in R2021b