深入理解sizeof():指针、数组、结构体和类中的陷阱详解

目录

一、引言

二、sizeof()基础

2.1、sizeof的本质

2.2、sizeof()基本语法

三、指针与sizeof

3.1、指针的基本行为

3.2、常见误区

四、数组与sizeof

4.1、数组的基本行为

4.2、数组长度计算技巧

4.3、常见陷阱

五、结构体与类中的sizeof

5.1 、内存对齐原则

5.2、 类的大小计算

5.3、继承与虚函数的影响

六、特殊场景分析

6.1、函数与sizeof

6.2、 不完全类型

6.3、位域

七、sizeof与strlen的区别

八、跨平台注意事项

九、实际应用技巧

9.1、安全的内存操作

9.2、 泛型编程中的应用

9.3、调试与断言

十、总结


一、引言

        在C/C++编程中,sizeof()操作符是一个看似简单但实则充满陷阱的工具。它用于计算对象或类型所占内存的字节数,但在不同场景下的行为却大相径庭。许多开发者,尤其是初学者,常常因为对其理解不够深入而写出错误的代码。本文将全面剖析sizeof()在应用于指针、数组、结构体和类时的各种行为,揭示其中的常见错误,帮助大家完全掌握它。

二、sizeof()基础

2.1、sizeof的本质

         sizeof()是C/C++中的一个操作符(operator),而非函数。它的返回值类型是size_t,在头文件中被定义为unsigned int类型。这个操作符在编译时而非运行时求值,这意味着它的结果在编译阶段就已经确定。

2.2、sizeof()基本语法

sizeof(类型)  // 必须加括号

使用示例:

int a;
sizeof(int);  // 正确
sizeof a;     // 正确但不推荐
sizeof int;   // 错误,类型必须加括号

三、指针与sizeof

3.1、指针的基本行为

        指针在32位系统中占4字节,在64位系统中占8字节,无论它指向什么类型的数据。这是许多初学者容易混淆的地方。

char* ss = "0123456789";
sizeof(ss);   // 32位系统返回4,64位系统返回8
sizeof(*ss);  // 返回1,因为*ss是char类型

3.2、常见误区

误区一:认为sizeof(指针)会返回指向内容的大小。

int* ptr = new int[100];
sizeof(ptr);  // 返回4或8,而不是400

 误区二:试图用sizeof计算动态分配数组的长度。

char* p = malloc(100);
sizeof(p);    // 仍然是4或8,无法得到分配的内存大小

四、数组与sizeof

4.1、数组的基本行为

        当sizeof应用于数组名时,它会返回整个数组占用的字节数,而不是指针的大小。

char ss[] = "0123456789";  // 包含'\0'共11个字符
sizeof(ss);  // 返回11

4.2、数组长度计算技巧

        常用计算数组元素个数的方法:

int arr[100];
int count = sizeof(arr) / sizeof(arr[0]);  // 正确计算元素个数

4.3、常见陷阱

 陷阱一:数组作为函数参数传递时会退化为指针。

void func(char arr[100]) {
    sizeof(arr);  // 返回指针大小,不是100
}

陷阱二:部分初始化数组时的误解。

int ss[100] = {1,2,3,4,5,6,7,8,9};
for(int i=0; i<sizeof(ss)/sizeof(int); i++) {
    // 会循环100次,而不是9次
}

五、结构体与类中的sizeof

5.1 、内存对齐原则

        ·为了CPU访问效率,编译器会对结构体和类进行内存对齐(Data Alignment)。对齐规则如下:

  1. 每个成员的偏移量必须是该成员大小的整数倍

  2. 整个结构体的大小必须是最大成员大小的整数倍

struct Example {
    char a;    // 1字节
    int b;     // 4字节
    short c;   // 2字节
};
// sizeof(Example) 在大多数系统中为12

5.2、 类的大小计算

类的大小计算需要考虑:

  • 非静态成员变量

  • 内存对齐

  • 虚函数表指针(如果有虚函数)

class X {
    int i;     // 4
    int j;     // 4
    char k;    // 1
};
// sizeof(X) 通常是12(考虑对齐)

5.3、继承与虚函数的影响

class Base {
    virtual void foo() {}
};
class Derived : public Base {
    int x;
};
// sizeof(Base) 通常包含虚表指针(4或8字节)
// sizeof(Derived) 包含基类部分和新增成员

六、特殊场景分析

6.1、函数与sizeof

  sizeof不能直接用于函数类型,但可以用于函数返回值:

short func() { return 0; }
sizeof(func());  // 返回sizeof(short)
sizeof(func);    // 编译错误

6.2、 不完全类型

  sizeof不能用于不完全类型(incomplete type):

extern int arr[];  // 不完全类型
sizeof(arr);       // 编译错误

6.3、位域

  sizeof不能用于位域成员:

struct {
    unsigned int a : 4;
};
sizeof(a);  // 错误

七、sizeof与strlen的区别

特性sizeofstrlen
类型操作符函数
参数类型或变量必须是char*
计算时机编译时运行时
包含'\0'
数组行为返回整个数组大小返回字符串长度

八、跨平台注意事项

  1. 指针大小:32位和64位系统不同

  2. 基本类型大小:不同平台可能有差异

  3. 对齐规则:不同编译器可能有不同实现

  4. 位域处理:实现定义行为

     最佳实践:永远不要假设类型的大小,使用sizeof获取实际大小。

九、实际应用技巧

9.1、安全的内存操作

// 安全的memset使用
SomeStruct s;
memset(&s, 0, sizeof(s));

9.2、 泛型编程中的应用

template<typename T, size_t N>
size_t array_size(T (&)[N]) {
    return N;
}

9.3、调试与断言

static_assert(sizeof(int) == 4, "int must be 4 bytes");

十、总结

  • 明确目的:确定你需要的是内存大小还是元素数量

  • 区分指针和数组:数组名在大多数情况下会退化为指针

  • 考虑对齐:结构体和类的大小可能大于成员总和

  • 跨平台思维:不要假设类型大小

  • 编译时检查:使用static_assert验证假设

  • 文档化假设:对大小敏感的代码要添加注释

         sizeof是一个强大的工具,但只有深入理解它的行为,才能避免陷入各种陷阱,写出健壮可靠的代码。通过深入理解sizeof的各种行为,开发者可以避免许多常见的错误,写出更加健壮和可移植的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大王算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值