Limesn
Limesn
发布于 2026-09-11 / 1 阅读
0

Sizeof 详解

sizeof 是 C/C++ 中的一元运算符,不是函数。它用来求某个类型或对象在内存中占用的字节数,结果类型是 size_t(无符号整数,定义在 <stddef.h> / <cstddef>)。

1. 基本语法

sizeof 表达式
sizeof(类型名)

例如:

sizeof(int)
sizeof(char)
sizeof a
sizeof(a)
sizeof(struct Student)

注意:

  • 类型使用时,括号通常是必须的:sizeof(int)
  • 表达式使用时,括号可省略:sizeof asizeof(a) 等价。
  • 括号不代表函数调用,sizeof 是运算符。

C++11 还有参数包版本:

sizeof...(Args)   // 返回参数包中元素个数,不是字节数

2. 关键性质

2.1 单位是“字节”,但标准中的字节是 char 的大小

C 标准规定:

sizeof(char) == 1

这里的“1 字节”是 char 所占的存储单元。通常一个字节是 8 位,但不绝对,具体由 CHAR_BIT 决定。

2.2 通常是编译期常量

在 C++ 标准中,sizeof 是编译期常量表达式,可用于:

static_assert(sizeof(int) == 4);
int arr[sizeof(int)];

在 C 中,除变长数组 VLA 外,sizeof 也是编译期常量。

2.3 一般不评估操作数

int i = 0;
size_t n = sizeof(i++);
printf("%d\n", i); // 仍然是 0

因为 sizeof 只关心 i++类型,不会真正执行 i++
例外:C99 的 VLA,如果操作数类型是变长数组,sizeof 可能在运行时计算。

2.4 返回类型是 size_t

打印时建议:

printf("%zu\n", sizeof(int)); // C99/C++11

C++ 中:

std::cout << sizeof(int) << '\n';

不要随便用 %d,因为 size_t 是无符号类型,宽度也可能不同。

3. 对数组和指针的区别

这是最经典的陷阱。

int a[10];

sizeof(a);      // 10 * sizeof(int),通常是 40
sizeof(a[0]);   // sizeof(int),通常是 4
sizeof(a + 0);  // sizeof(int*),指针大小,通常是 8

关键点:

  • a 是数组名,在 sizeof不会退化为指针,所以得到整个数组大小。
  • a + 0 是表达式,数组名退化为指针,所以得到指针大小。
  • 数组做函数参数时会退化为指针:
void f(int a[10]) {
    printf("%zu\n", sizeof(a)); // 指针大小,不是 40
}

如果真想传数组并保留大小,可以用:

void f(int (*a)[10]) {
    printf("%zu\n", sizeof(*a)); // 整个数组大小
}

C++ 中可写:

template <size_t N>
void f(int (&a)[N]) {
    std::cout << sizeof(a) << '\n'; // N * sizeof(int)
}

4. 字符串字面量

char s[] = "hello";
char *p = "hello";

sizeof(s); // 6,包含结尾 '\0'
strlen(s); // 5,不包含 '\0'

sizeof(p); // 指针大小
strlen(p); // 5

sizeof 是求内存占用,strlen 是运行时求字符串长度。

5. 结构体、对齐和填充

sizeof(struct) 不一定等于成员大小之和,因为存在内存对齐和填充

struct A {
    char c;
    int i;
};

典型 64 位平台上:

  • c 在偏移 0;
  • 为了 int 对齐,偏移 1~3 填充;
  • i 在偏移 4;
  • 总大小通常为 8。
struct B {
    int i;
    char c;
};

通常也是 8,因为结构体总大小要按最大对齐数补齐。

常见规则:

  • 每个成员按自身对齐要求放在合适偏移;
  • 结构体总大小通常是最大成员对齐数的整数倍;
  • 可用 #pragma pack 改变对齐,但可能降低访问效率;
  • 可用 offsetof 查成员偏移,用 alignof / _Alignof 查对齐。

位域不能直接 sizeof

struct BF {
    unsigned a : 1;
    unsigned b : 2;
};

// sizeof(BF.a); // 错误

sizeof(struct BF) 是合法的,表示整个位域结构体大小。

6. C++ 中的类对象大小

C++ 中 sizeof 对类类型返回对象本身大小,不包括动态分配内存。

struct Empty {};

sizeof(Empty); // 通常为 1

空类大小不是 0,因为每个对象必须有唯一地址。

struct A {
    int x;
    static int y;
    void f();
};

sizeof(A) 通常等于 sizeof(int)
静态成员、成员函数不占对象大小。

有虚函数时,通常会多一个虚表指针:

struct B {
    virtual void f();
};

64 位平台上 sizeof(B) 通常为 8,即一个指针大小。

struct C {
    virtual void f();
    int x;
};

64 位平台上通常为 16:8 字节 vptr + 4 字节 int + 4 字节填充。

C++ 还有空基类优化:

struct Empty {};
struct D : Empty {
    int x;
};

sizeof(D); // 通常等于 sizeof(int)

引用:

int x = 0;
int& r = x;

sizeof(r);   // sizeof(int)
sizeof(int&); // 通常也等于 sizeof(int)

sizeof 对引用得到的是被引用类型的大小。

7. C 和 C++ 的一些差异

字符常量

sizeof('a');
  • C 中字符常量类型是 int,所以 sizeof('a') == sizeof(int),通常为 4。
  • C++ 中字符常量类型是 char,所以 sizeof('a') == 1

空结构体/空类

  • C++:空类 sizeof 通常为 1。
  • C:空结构体不是标准用法,GCC 扩展下可能为 0。

VLA

  • C99 支持变长数组,sizeof 可能在运行时计算。
  • C++ 标准不支持 VLA,只是部分编译器扩展。

sizeof(void)

标准 C/C++ 都不允许。
GCC 等扩展中 sizeof(void) == 1,但不要依赖。

8. 常见非法用法

struct Node;
sizeof(struct Node); // 错误:不完整类型

sizeof(main); // 错误:函数类型

struct BF { unsigned a : 1; };
sizeof(BF.a); // 错误:位域

函数指针可以:

sizeof(&main); // 指针大小

9. 常见陷阱总结

  1. 数组名在 sizeof 中不退化为指针,但作为函数参数会退化。

  2. sizeof(指针) 得到指针大小,不是它指向的内存大小。

  3. malloc 后:

    int *p = malloc(100 * sizeof(int));
    sizeof(p); // 指针大小,不是 400
    
  4. sizeof 包含结构体填充,不等于成员大小简单相加。

  5. sizeof 不包含动态分配内存,只算对象本身。

  6. sizeof 结果是无符号 size_t,和负数比较容易出问题。

  7. 平台相关:intlong、指针大小不一定相同。

10. 一句话记忆

sizeof 是运算符,不是函数;它求类型或对象占用的字节数,通常编译期确定,不计算表达式,结果类型是 size_t。对数组得整个数组大小,对指针得指针大小,对结构体/类包含对齐填充,但不包含动态分配内容。