C ++ struct成员内存分配

时间:2015-02-15 13:31:40

标签: c++ memory struct interpreter

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

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

我正在写一个简单的解释器,这个“rtok”结构就是我代表一个标记的方式。我有一个“rtoks”向量,我迭代生成解析树。

我的问题是,如果我的结构中有3个成员且我只给1个成员一个值,那么其他成员是否还会占用记忆?

我的意思是,如果我将“val”设置为“test”,我的令牌只占用4个字节还是会占用6个字节? (“val”为4个字节,类型为1个字节,术语为1个字节)

4 个答案:

答案 0 :(得分:5)

假设您没有其他成员或虚拟功能,您的结构将始终占用sizeof(char) + sizeof(string) + sizeof(bool) + possible paddingstring部分为自己分配了一块内存,它在销毁时解除分配。但是,此内存在技术上不是为struct分配的内存的一部分。

因此,无论您为成员提供(或省略)值,结构总是具有相同的大小。

答案 1 :(得分:2)

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

有两个因素:数据对齐和内部类型实现。 首先,关于数据对齐:结构中的所有类型都自然地对齐,这意味着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个字节。

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

答案 2 :(得分:1)

给定类型的struct总是具有相同的大小。这是标准的保证。当你定义一个struct时,你会说“我有一个这个大小的对象(成员大小的总和+每个成员对齐的可能填充),它们将在内存中的此顺序(包含struct定义的成员定义的顺序相同)“:

  

(N4296)   9.2
  / 12 [示例:类定义的一个简单示例是

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

包含一个由二十个字符组成的数组,一个整数和两个指向同一类型对象的指针。 [...] - 示例

  

/ 13分配具有相同访问控制(第11条)的(非联合)类的非静态数据成员,以便后面的成员在类对象中具有更高的地址。具有不同访问控制的非静态数据成员的分配顺序未指定(第11条)。实施对齐要求   可能导致两个相邻成员不能立即分配;所以可能需要空间来管理虚函数(10.3)和虚基类(10.1)。

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

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

public_2的地址高于public_1。除此之外 - 未说明。 private_1可能位于较低或较高的地址。

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

  

那么使用类而不是结构会更好吗?

在C ++中,structclass基本相同,唯一的区别是struct的成员(和继承)默认为public,而使用class默认为private。在标准的注释和示例中,这一点更加清晰:

  

§3.1声明和定义[basic.def]
  / 3 [注意:在某些情况下,C ++   实现隐式定义默认构造函数(12.1),copy   构造函数(12.8),移动构造函数(12.8),复制赋值运算符(12.8),移动赋值运算符(12.8)或析构函数(12.4)成员函数。 - 注意] [示例:给出

#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;
}
  

实现将隐式定义函数以使C的定义等效于

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() { }
};
  

- 示例]

请注意,标准中的示例使用struct而不是class来说明非POD structs的这一点。当您考虑标准中struct的定义在第9节 - “类”中时,这一点就更加清晰了。

答案 3 :(得分:-1)

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

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