C++ sizeof与strlen,并借此明晰内存对齐

发布时间 2023-03-25 15:48:32作者: 石中火本火

前言

sizeof()strlen()都是为了获取对象的长度。在正常编写C++的算法程序代码时,可能这两个都很少用到,因为各种stl容器的封装已经给了我们很大的便利,比如我们在想要获取自定义的vector容器里的元素个数,想要获取string类型的字符串的长度时,都只需要调用他们的.size()方法就可以得到容器的长度或者元素的个数。诚然如此,仍会有很多情况需要获取未定义大小方法的对象的大小,比如一个结构体,所以我们还是需要关注这些偏底层的知识,下面就来细细阐述:

sizeof()使用

sizeof() 计算的是变量或类型所占用的内存字节数, 而strlen计算的只能是变量的长度;

#include<iostream>
using namespace std;
struct Node
{
    int val1;  // 4 bytes
    int val2; // 4 bytes
    Node* left; // 8bytes
    Node* right; // 8 bytes
};

int main(){
    cout<<"int:"<<sizeof(int)<<endl;  // 4 bytes
    cout<<"char:"<<sizeof(char)<<endl; // 1 bytes
    cout<<"int*:"<<sizeof(int*)<<endl; // 8 bytes
    cout<<"char*:"<<sizeof(char*)<<endl; // 8 bytes
    cout<<"Node*:"<<sizeof(Node*)<<endl; // 8 bytes
    int x;  
    cout<<"int x:"<<sizeof(x)<<endl;    // 4 bytes
    char s[] = "123 456";
    cout<<"s[]:"<<sizeof(s)<<endl;    // 8 bytes, including '\0'
    Node* node = new Node();
    cout<<"node:"<<sizeof(*node)<<endl; // 24 bytes, or 8 bytes if sizeof(node), because node is just a pointer.
    return 0;
}

总结:
sizeof对于所有的指针,无论指针类型是什么,其所计算的都是指针本身也就是存储的地址值的长度,而对于64位机器,8 bytes足以访问其所有的内存地址(32位机器是4 bytes)。
而对于字符数组类型,会自动计算在其末尾的'\0'运算符,是比strlen()多一个单位的。
对于一个自定义结构体,会自动计算所有成员变量的和。从上可以看出对于两个int类型,两个指针类型他们的字节数分别是4, 4, 8, 8,于是总大小为24bytes。但此时有一个问题,如果把第二个int换为char,答案就是21 bytes了吗?答案是否定的,仍然是24 bytes,这就是内存对齐所导致的结果,sizeof()的值就是对齐之后的值。
关于对齐可以参考:https://zhuanlan.zhihu.com/p/30007037
此处放几个截图可以清晰的说明内存如何对齐(以四字节为对齐标准):

image
其对齐后的内存布局分别如下:
image

另外还需注意的是关于最后是否补齐为4或8字节的整数倍还是依具体平台而定。自测如下结构体,只占3字节并未补齐,但是运行x2却发现又补齐了,颇为奇怪:

struct Test{
    char c1;
    char c2;
    char c3;
}test;

strlen()使用

strlen()是函数,只能去计算以空字符'\0'结尾的字符串,否则是未定义的行为,所以他无法去计算结构体的大小。定义如下:

size_t strlen ( const char * str );
(参数中只能传递字符串类型)

具体使用:

char s[] = "Hello, world!";
strlen(s) // 输出 13,即字符串 s 中有 13 个字符(不包括结尾的空字符 '\0')

区别

那么除了上述用法和结果上的一些区别,即sizeof可以用在任意类型与对象,统计全部的字符大小包括'\0', 而strlen只能统计字符串的长度(不统计'\0').在底层实现时也有一些区别:

sizeof()是运算符,strlen()是库函数
sizeof()在编译时计算好了,strlen()在运行时计算
sizeof()计算出对象使用的最大字节数,strlen()计算字符串的实际长度
sizeof()的参数类型多样化(数组,指针,对象,函数都可以),strlen()的参数必须是字符型指针(传入数组时自动退化为指针)

其实两者之前共同点就只在于都可以计算字符数组的长度,其他尽不相同。