C++中的sizeof()运算符是做什么用的?

3
在C语言中,sizeof()操作符可以在编译时获取其操作数的大小。它不会对操作数进行求值。例如:
int ar1[10];
sizeof(ar1) // output 40=10*4
sizeof(ar1[-1]) // output 4
int ar2[ sizeof(ar1) ]; // generate an array of 40 ints.

在处理C++模板类时,我发现了一些奇怪的结果。

template<typename T>
struct S{
    T a;
};

sizeof( S<int> )       // output 4
sizeof( S<bool> )      // output 1
sizeof( vector<int> )  // output 24
sizeof( vector<char> ) // output 24
sizeof( vector<bool> ) // output 40

我猜vector或其他STL容器上的sizeof取决于特定的环境。

问题1. C/C++中如何实现sizeof?它不能是运行时函数。它是一个宏吗?(我在在线教程视频中学到的)。如果它是一个宏,它的#define是什么样子的?sizeof()何时执行?

问题2. 如果我向struct S的定义中添加一个成员方法void f(){}sizeof(S<int>)仍然为4。这个结构体的大小不应该增加吗?

问题3. STL容器是模板类。以vector为例,它有12个成员属性/类型和许多成员方法。很容易解释sizeof( S<int> )的输出。但我发现很难解释sizeof( vector<int> )的输出。模板类应该在编译时被实例化,编译器应该完全知道类的大小,即vector<int>。因此,sizeof()操作符应该知道。


2
你一次问了太多问题。第二个问题在这里得到了回答:https://dev59.com/BWsz5IYBdhLWcg3wNE9q - Slava
sizeof是C/C++中的一个关键字。我从未见过宏的名称变成关键字。也许sizeof不是宏。 - Peng Zhang
@Slava 谢谢。老实说,我不会在我的代码中以那种奇怪的方式使用 sizeof。只是感觉对 sizeof 的行为太无知了。 - Peng Zhang
哪个视频说sizeof是一个宏? - chris
@chris 这个问题在这个中文视频中有解释:http://edu.51cto.com/lesson/id-30061.html - Peng Zhang
sizeof(int)或sizeof(char)的名称表明,它们分别分配给定类型的内存,其字节大小为int或bol或string。 int大小为4个字节,bol大小为1个字节。 因此,如果您创建一个包含10个元素的int数组,则分配的总字节数将为10 * 4 = 40个字节长。 - Juniar
2个回答

6
根据问题1:sizeof是由编译器实现和计算的,它不是一个宏,而且它总是提供编译时结果。从概念上讲,你可以想象编译器将每个sizeof替换成一个数字。
根据问题2:sizeof计算一个实例占用的存储空间。一个方法不占用每个实例的存储空间,只有字段占用(因为它们每个实例中只存在一次)。但是一个方法占用静态存储空间来保存函数的机器码。
根据问题3:对于sizeof(vector),编译器计算vector的大小,为了这么做,它会实例化vector。如果你感到困惑,因为vector的大小是可变的:这是正确的,但额外的存储空间是从堆分配的,因此不反映在应用于vector的sizeof的结果中。

@Slava,这应该被视为“实现相关”或“事实标准”,关键是这种要求是由虚函数的实现细节所强制的,而不是由标准所规定的虚函数本身。 - user2485710
3
std::vector<bool>是一个明确指定的特化版本,可能会使用比普通向量更多的空间。 - Mark B
1
@PengZhang vector<bool> 是一种非常奇怪的数据结构,它基本上是标准 C++ 库中规则的少数例外之一;最好跳过它。 - user2485710
1
@PengZhang,这些是类型别名。它们不占用每个实例的空间。 - chris
1
@PengZhang,typedef#define 不能可靠地做到这一点。这远不止于C++ STL。关键是你可以说 vector<int>::size_type 来获取例如 size() 返回的类型。实际数据成员的数量取决于实现。 - chris
显示剩余6条评论

5
  1. sizeof 不是一个宏。它是一种内置的运算符,产生一个常量表达式,因此可以想象编译器只是在编译时用值为对象或类操作数大小的字面量替换它。

  2. 函数不占据对象内部的空间。函数的代码不存储在对象内部。

  3. 容器(例如 std::vector<int>)的大小是向量成员占用的内存量加上一些实现定义的填充。它不包括向量通过持有指针所“拥有”的任何内存。(请注意,向量的元素不是成员——当我说成员时,指的是在 std::vector 类的定义中声明的成员。)因此,它与向量内有多少元素无关。


函数不占用对象内部的空间。函数的代码不存储在对象内部 - 好吧,我只是好奇。我相信对象仍然必须持有指向其函数的指针,如果是这样,那么这个指针会占用少量的内存空间,对吗? - Andrey Chernukha
2
@AndreyChernukha 不需要。如果函数不是虚函数,编译器可以在编译时确定其地址;调用成员函数不需要检查对象。如果函数是虚函数,则通常只会向对象添加一个指针,该指针指向存储在其他地方的表格,该表格给出了对象类的所有虚函数的地址。 - Brian Bi
非常感谢您的好回答。很难选择其他的。 - Peng Zhang

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接