C++11alignas 说明符和alignof 运算符和内存对齐问题

alignas 说明符

语法

alignas( 表达式 )
alignas( 类型标识 )
alignas( 包 ... )

1) 表达式 必须是求值为零或合法的对齐或扩展对齐的整型常量表达式。

2) 等价于 alignas(alignof(类型标识))

3) 等价于对同一说明应用多个 alignas 说明符,逐个对应于形参包的各个成员,形参包可以是类型或非类型形参包。

解释

alignas 说明符可用于:

  • 类的声明或定义;
  • 非位域类数据成员的声明;
  • 变量声明,除了它不能应用于下列内容:
    • 函数形参;
    • catch 子句的异常形参。

这种声明所声明的对象或类型的对齐要求https://zh.cppreference.com/w/cpp/language/object#.E5.AF.B9.E9.BD.90(即 每个对象类型都具有被称为对齐要求(alignment requirement)的性质,它是一个整数(类型是 std::size_t,总是 2 的幂),表示这个类型的不同对象所能分配放置的连续相邻地址之间的字节数。)

将等于用于该声明的所有 alignas 说明符中最严格(最大)的非零 表达式,除非这会削弱类型的自然对齐。

如果某个声明上的最严格(最大)alignas 比当它没有任何 alignas 说明符的情况下本应有的对齐更弱(即弱于其原生对齐,或弱于同一对象或类型的另一声明上的 alignas),那么程序非良构:

struct alignas(8) S {};
struct alignas(1) U { S s; }; // 错误:如果没有 alignas(1) 那么 U 的对齐将会是 8

无效的非零对齐,例如 alignas(3) 非良构。

同一声明上,比其他 alignas 弱的有效的非零对齐被忽略。

始终忽略 alignas(0)。

alignof 运算符

查询类型的对齐要求。

语法

alignof( 类型标识 )

返回 std::size_t 类型的值。

解释

返回由类型标识所指示的类型的任何实例所要求的对齐字节数,该类型可以是完整对象类型、元素类型完整的数组类型或者到这些类型之一的引用类型。

如果类型是引用类型,那么运算符返回被引用类型的对齐要求;如果类型是数组类型,那么返回元素类型的对齐要求。

#include 

struct Foo
{
    int   i;
    float f;
    char  c;
};

// 注:下面的 `alignas(alignof(long double))`
// 如果需要可以简化为  `alignas(long double)`
struct alignas(long double) Foo2//这里其实就是相当于设置了内存对齐为8了
{
    // Foo2 成员的定义...
};

struct Empty {};

struct alignas(64) Empty64 {};

int main()
{
    std::cout << "对齐字节数"  "\n"
        "- char                :" << alignof(char) << "\n"
        "- 指针                :" << alignof(int*) << "\n"
        "- Foo 类              :" << alignof(Foo) << "\n"
        "- Foo2 类             :" << alignof(Foo2) << "\n"
        "- 空类                :" << alignof(Empty) << "\n"
        "- 带 alignas(64) 的空类:" << alignof(Empty64) << "\n";
    std::cout << "- Foo 类字节大小              : " << sizeof(Foo) << "\n";
}

    我们要区分sizeof和alignof,sizeof运算符获取字节大小,alignof获取的是对齐

空类

很多人认为空类大小就是1,看过我们上面aligas后估计会想到可以用这个设置一下,但这都是不是什么重点,我们来强调一下概念。

为保证同一类型的不同对象地址始终有别,要求任何对象或成员子对象的大小至少为 1,即使该类型是空的类类型(即没有非静态数据成员的 class 或 struct)(,除非带有 [[no_unique_address]]) (C++20 起)。

#include 
#include

struct Base {}; // 空类

struct Derived1 : Base {
    
};
struct Derived3 : Base {
    Derived1 c; // 从 Base 派生,占用 sizeof(int) 字节
    int i;
};

struct Empty {}; // 空类

struct X {
    int i;
    [[no_unique_address]] Empty e;/*如果空成员子对象使用属性 [[no_unique_address]],那么允许像空基类一样优化掉它们。取这种成员的地址会产生可能等于同一个对象的某个其他成员的地址*/
};
int main()
{

    std::cout << sizeof(Derived1) << std::endl;//正常的1,优化空基类

    std::cout << sizeof(Derived3) << std::endl;//正常的8,空基类优化掉,4+1,对齐就是8

    std::cout << sizeof(X) << std::endl;//文档的说法是”允许优化掉“,那么打印的是4,但MSVC并没有这么做,还是8(GCC是4),注意这是c++20的
}

c++20这个属性不会的可以先不看,总而言之就是,要求至少为1,没说为1,凡事严谨点总是好的。

总结一下就是,说起空类的大小,你首先要考虑有没有alignas,有没有[[no_unique_address]],然后就能说“多数情况下,是1”

总结

qq交流群:829810543  回复:C++ 

alignas 说明符 (C++11 起) - cppreference.comhttps://zh.cppreference.com/w/cpp/language/alignasalignof 运算符(C++11 起) - cppreference.comhttps://zh.cppreference.com/w/cpp/language/alignof 对象 - cppreference.comhttps://zh.cppreference.com/w/cpp/language/object#.E5.AF.B9.E9.BD.90空基类优化 - cppreference.comhttps://zh.cppreference.com/w/cpp/language/ebo

你可能感兴趣的:(c++,算法,开发语言)