1

我希望能够找到结构中各个成员的大小。例如

struct A {
    int  a0;
    char a1;
}

现在 sizeof(A) 为 8,但假设我正在编写一个函数,该函数将打印 A 的对齐方式,如下所示,其中“aa”表示填充。

数据 A:
0x00:00 00 00 00
0x04:00 aa aa aa
*-------------------------
大小:8 填充:3

为了让我计算填充,我需要知道结构中每个单独成员的大小。所以我的问题是如何访问给定结构的各个成员。

另外,让我知道是否有另一种方法可以找到填充的数量。

4

4 回答 4

1

一种简单的方法是使用sizeof运算符(利用它不评估其操作数的事实,仅确定评估它时将产生的类型的大小)和offsetof()宏(来自<cstddef>)。

例如;

#include <iostream>
#include <cstddef>

struct A
{
    int  a0;
    char a1;
};

int main()
{
    // first calculate sizes
    size_t size_A = sizeof(A);
    size_t size_a0 = sizeof(((A *)nullptr)->a0);    // sizeof will not dereference null
    size_t size_a1 = sizeof(((A *)nullptr)->a1);

    //   calculate positions

    size_t pos_a0 = offsetof(A, a0);    //  will be zero, but calculate it anyway
    size_t pos_a1 = offsetof(A, a1);

    // now calculate padding amounts
    size_t padding_a0 = pos_a1 - pos_a0 - size_a0;    // padding between a0 and a1 members
    size_t padding_a1 = size_A - pos_a1 - size_a1;

    std::cout << "Data A:\n";
    std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << pos_a0;
    size_t i = pos_a0;
    while (i < pos_a0 + size_a0)      // print out zeros for bytes of a0 member
    {
        std::cout << " 00";
        ++i;
    }

    while (i < pos_a1)      //  print out aa for each padding byte after a_0
    {
        std::cout << " aa";
        ++i;
    }
    std::cout << std::endl;

    std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << pos_a1;

    while (i < pos_a1 + size_a1)      // print out zeros for bytes of a1 member
    {
        std::cout << " 00";
        ++i;
    }

    while (i < size_A)      //  print out aa for each padding byte after a_1
    {
        std::cout << " aa";
        ++i;
    }
    std::cout << std::endl;
    std::cout << "size: " << size_A << " padding: " << padding_a0 + padding_a1 << std::endl;


}
于 2020-09-26T08:29:21.680 回答
0

如果您知道结构的内容,您可以解决这个问题。传递通常是这样工作的,假设你的结构是这样的。

struct A {
    int  a0;    // 32 bits (4 bytes)
    char a1;    // 8 bits (1 byte)
};

但这不是内存效率,就像您将这些结构打包在内存中一样,您可能会遇到一些碎片问题,从而使应用程序变慢。所以编译器像这样优化结构,编译器的最终结构看起来像这样。

struct A {
    int  a0;
    char a1;
    
    // padding
    char __padd[3]; // 3 * 1 byte = 3 bytes.
    /// Adding this padding makes it 32 bit aligned. 
};

现在使用这些知识,您可以了解为什么在不知道对象内容的情况下很难获得对象的填充。并且填充并不总是放置在对象的末尾。例如,

struct Obj {
    int a = 0;
    char c = 'b';
    // Padding is set here by the compiler. It may look something like this: char __padd[3];
    int b = 10;
};

那么如何获得结构的填充呢?
您可以使用称为反射的东西在运行时获取结构的内容。然后锻炼结构中数据类型的大小,然后您可以通过减去前一个类型和下一个类型的大小来计算填充,从而为您提供填充的外观。

于 2020-09-26T07:06:27.070 回答
0

正如其他答案所说,offsetof宏显然是这里最好的解决方案,但只是为了证明您可以通过查看指针在运行时找到成员的位置:

#include <iostream>

struct A
{
    char x;
    int y;
    char z;
};

template <typename T>
void PrintSize ()
{
    std::cout << "    size = " << sizeof(T) << std::endl;
}

void PrintPosition (char * ptr_mem, char * ptr_base)
{
    std::cout << "    position = " << ptr_mem - ptr_base << std::endl;
}

template <typename T>
void PrintDetails (char member, T * ptr_mem, A * ptr_base)
{
    std::cout << member << ":" << std::endl;

    PrintSize<T>();
    PrintPosition((char*) ptr_mem, (char*) ptr_base);
    
    std::cout << std::endl;
}

int main()
{
    A a;
    
    PrintDetails('x', &a.x, &a);
    PrintDetails('y', &a.y, &a);
    PrintDetails('z', &a.z, &a);
}

我机器上的输出:

x:
大小 = 1
位置 = 0

y:
大小 = 4
位置 = 4

z:
大小 = 1
位置 = 8

(令人惊讶的是,在我的 intel 上,使用 gcc/clang,A大小为 12!我认为编译器在重新排列元素方面做得更好)

于 2020-09-26T09:15:19.433 回答
-2

要计算结构的填充,您需要知道最后一个成员的偏移量和大小:

简而言之,如果 typeT有一个 type 的成员lastU则填充大小为:

sizeof(T) - (offsetof(T, last) + sizeof(U))

要计算结构中的填充总量,如果这就是这个问题的内容,我会使用 GCC 扩展:两次声明相同的结构(可能在宏的帮助下),一次没有packed属性,一次有。然后减去它们的大小。

这是一个完整的工作示例:

#include <stdio.h>
  
#define X struct { char a; int b; char c; }

int main(void)
{
  printf("%zd\n", sizeof(X) - sizeof(X __attribute__((packed))));
  return 0;
}

对于上述结构,它输出 6。这对应于结构a的四字节对齐所必需的 3 字节填充b和结构末尾的填充,b如果结构用作数组成员,则对齐所必需。

packed属性破坏了所有填充,因此打包和解包结构之间的差异为我们提供了填充的总量。

于 2020-09-26T06:36:17.720 回答