C++结构成员内存分配

C++ struct member memory allocation

我有一个看起来像这样的结构:

struct rtok {
    char type;
    std::string val;
    bool term;
};

我正在编写一个简单的解释器,这个 "rtok" 结构是我表示令牌的方式。我有一个 "rtoks" 的向量,我迭代它以生成解析树。

我的问题是,如果我的结构中有3个成员,我只给1个成员赋值,其他成员是否还会占用内存?

我的意思是,如果我设置 "val" 等于 "test",我的令牌会占用 4 个字节还是占用 6 个字节? (4 个字节用于 "val",1 个字节用于类型,1 个字节用于术语)

假设您没有额外的成员或虚函数,您的结构将始终占用 sizeof(char) + sizeof(string) + sizeof(bool) + possible paddingstring 部分为自己分配了一块内存,并在销毁时将其释放。但是,从技术上讲,此内存并不是为 struct 分配的内存的一部分。

因此无论您为成员提供(或省略)任何值,该结构将始终具有相同的大小。

别担心,这比您想象的要多得多。

有两个因素:数据对齐和内部类型实现。 首先,关于数据对齐:你的结构中的所有类型都是自然对齐的,这意味着char可以在任何地址,但是void*可能需要对齐4 或 8,具体取决于体系结构。

所以,如果我们猜测,std::string 在内部简单地使用 char* 来让你串起来 x32 上的布局将是:

struct rtok {
  char type;
  char* val; // here char * for simplicity
  bool term;
};

sizeof(rtok) 运算符将给出 12 个字节,而不是 6 个,内存占用量如下所示:

00: type (one byte)
01: padding
02: padding
03: padding
04-07: char * (4 bytes)
08: term (one byte)
09-0a: padding (3 bytes)

现在,如果我们将 char* 替换为 std::string,我们会发现结构大小增加了,因为 sizeof(std::string) 通常大于 4 个字节。

但是,我们还没有计算字符串值本身...这里我们进入堆管理和分配领域。

存储值的内存分配在堆上,代码通常请求多少就请求多少,所以对于 10 个字符的字符串,它将是 11 个字节(10 个字符加上空终止符 1 个字节)。

并且堆具有自己的复杂结构,包括小块堆等。在实践中,这意味着最小消耗量大约为 16 字节或更多。这个数量不是你能用的,这个数量是用来管理堆内部结构的,唯一可以使用的数量只有1个字节。

如果你把所有东西加起来,你会发现即使你打算只使用两个字符加上类型,消耗的内存量也会大得多。

如前所述,struct 始终是固定大小的。 有几种方法可以克服这个限制:

  1. 存储指针并为其分配堆内存。
  2. 使用char[1]的"unbound"数组作为最后一个成员,并在堆上为struct本身分配内存。
  3. 使用 union 为重叠成员节省一些 space。

给定类型的 struct 始终具有相同的大小。这是标准的保证。当您定义 struct 时,您说的是 "I have an object of this size (sum of sizes of members + possible padding for alignment for each member), and they are to be in this order in memory (same order of member definitions in containing struct definition)":

(N4296) 9.2
/12 [ Example: A simple example of a class definition is

struct tnode {
  char tword[20];
  int count;
  tnode* left;
  tnode* right;
};

其中包含一个包含二十个字符的数组、一个整数和两个指向相同类型对象的指针。 [...] -结束示例

/13 Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (Clause 11). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).

注意“具有相同的访问控制”限定符。如果您的结构混合了具有不同访问说明符的数据成员,则布局可能不是您所期望的,除了给出如下内容的保证:

public:
   some_type public_1;
private:
   some_type private_1;
public:
   some_type public_2;

public_2 将位于比 public_1 更高的地址。除此之外 - 未指定。 private_1 可能位于较低或较高的地址。

关于您的其他问题(在评论中提出):

Would it be better to use a class instead of a struct then?

在 C++ 中,structclass 本质上是相同的,唯一的区别是 struct 的成员(和继承)默认为 public ,而对于 class,它们默认为 private。这在标准的注释和示例中更加清楚:

§3.1 Declarations and definitions [basic.def]
/3 [ Note: In some circumstances, C ++ implementations implicitly define the default constructor (12.1), copy constructor (12.8), move constructor (12.8), copy assignment operator (12.8), move assignment operator (12.8), or destructor (12.4) member functions. —end note ] [ Example: given

#include <string>
struct C {
    std::string s; // std::string is the standard library class (Clause 21)
};
int main() {
    C a;
    C b = a;
    b = a;
}

the implementation will implicitly define functions to make the definition of C equivalent to

struct C {
    std::string s;
    C() : s() { }
    C(const C& x): s(x.s) { }
    C(C&& x): s(static_cast<std::string&&>(x.s)) { }
    // : s(std::move(x.s)) { }
    C& operator=(const C& x) { s = x.s; return *this; }
    C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; }
    // { s = std::move(x.s); return *this; }
    ~C() { }
};

—end example ]

请注意,标准中的示例使用 struct 而不是 class 来说明非 POD structs 的这一点。当您考虑到标准中 struct 的定义在第 9 节 - "Classes."

时,这一点就更加清楚了