C++ C++;结构成员内存分配

C++ C++;结构成员内存分配,c++,memory,struct,interpreter,C++,Memory,Struct,Interpreter,我有一个结构,看起来像这样: struct rtok { char type; std::string val; bool term; }; 我正在编写一个简单的解释器,这个“rtok”结构就是我表示令牌的方式。我有一个“rtoks”向量,我通过它迭代生成解析树 我的问题是,如果我的结构中有3个成员,我只给1个成员赋值,那么其他成员还会占用内存吗 我的意思是,如果我将“val”设置为“test”,我的令牌会占用4个字节还是6个字节?(4个字节表示“val”,1个字节表示

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

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)+可能的填充。
字符串
部分为自己分配一块内存,并在销毁时释放。但是,从技术上讲,该内存不是为
结构分配的内存的一部分


因此,无论您为成员提供(或忽略)什么值,结构都将始终具有相同的大小。

假设您没有额外的成员或虚拟函数,您的结构将始终占用
sizeof(char)+sizeof(string)+sizeof(bool)+可能的填充。
字符串
部分为自己分配一块内存,并在销毁时释放。但是,从技术上讲,该内存不是为
结构分配的内存的一部分


因此,无论您为成员提供(或忽略)什么值,结构都将始终具有相同的大小。

不要担心,它将比您想象的要多得多

有两个因素:数据对齐和内部类型实现。 首先,关于数据对齐:结构中的所有类型都是自然对齐的,这意味着
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)
现在,如果我们用
std::string
替换
char*
,我们会发现结构大小已经增加,因为
sizeof(std::string)
通常大于4个字节

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

用于存储值的内存是在堆上分配的,代码通常会请求它所需的内存,因此对于10个字符的字符串,它将是11个字节(10个字符加1个字节表示空终止符)

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


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

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

有两个因素:数据对齐和内部类型实现。 首先,关于数据对齐:结构中的所有类型都是自然对齐的,这意味着
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)
现在,如果我们用
std::string
替换
char*
,我们会发现结构大小已经增加,因为
sizeof(std::string)
通常大于4个字节

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

用于存储值的内存是在堆上分配的,代码通常会请求它所需的内存,因此对于10个字符的字符串,它将是11个字节(10个字符加1个字节表示空终止符)

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


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

正如前面所说,
struct
的大小总是固定的。 有几种方法可以克服此限制:

  • 存储指针并为其分配堆内存
  • 使用
    char[1]
    的“unbound”数组作为最后一个成员,并为堆上的
    struct
    本身分配内存
  • 使用
    union
    为重叠的成员节省一些空间

  • 正如前面所说,
    struct
    的大小总是固定的。 有几种方法可以克服此限制:

  • 存储指针并为其分配堆内存
  • 使用
    char[1]
    的“unbound”数组作为最后一个成员,并为堆上的
    struct
    本身分配内存
  • 使用
    union
    为重叠的成员节省一些空间

  • 给定类型的
    struct
    始终具有相同的大小。这是标准的保证。当您定义一个
    结构时,您的意思是“我有一个这样大小的对象(成员大小的总和+每个成员对齐的可能填充),它们在内存中的顺序是这样的(与包含
    结构中的成员定义的顺序相同
    
    #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;
    }
    
    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() { }
    };