过去有一段时间一直以为带个括号的 (sizeof()) 是 (C/C++) 的原生函数QAQ。
其实不然,(sizeof) 同位运算符(^|&~!)一样是一种单目运算符,作用于变量或数组。
在编译时编译器就会把 (sizeof()) 的内容转换成常数存入机器码中,不涉及函数的底层操作。
用途
sizeof 运算符可用于获取类、结构、共用体和其他用户自定义数据类型的大小。
使用 sizeof 的语法如下:
sizeof (data type)
其中,data type 是要计算大小的数据类型,包括类、结构、共用体和其他用户自定义数据类型。
sizeof 与 普通变量
#include<bits/stdc++.h>
using namespace std;
int main() {
char char_var;
float float_var;
double double_var;
long double ldouble_var;
int int_var;
long long ll_var;
printf("sizeof char = %d
",sizeof(char_var));
printf("sizeof float = %d
",sizeof(float_var));
printf("sizeof double = %d
",sizeof(double_var));
printf("sizeof long double = %d
",sizeof(ldouble_var));
printf("sizeof int = %d
",sizeof(int_var));
printf("sizeof long long = %d
",sizeof(ll_var));
return 0;
}
输出结果:
sizeof char = 1
sizeof float = 4
sizeof double = 8
sizeof long double = 16
sizeof int = 4
sizeof long long = 8
可以看出,编译器把 sizeof 的内容都替换成了变量占用的空间大小(单位为字节)。
此外,作为一个运算符,在 sizeof 之后的变量名可以不用括号括起来,这样在概念上就不会和函数混淆了。
sizeof 与 指针变量
#include<bits/stdc++.h>
using namespace std;
int main() {
char *char_point;
double *double_point;
int *int_point;
long long ll_point;
printf("sizeof pchar = %d
",sizeof(char_point));
printf("sizeof pdouble = %d
",sizeof(double_point));
printf("sizeof pint = %d
",sizeof(int_point));
printf("sizeof plong long = %d
",sizeof(ll_point));
return 0;
}
输出结果:
sizeof pchar = 8
sizeof pdouble = 8
sizeof pint = 8
sizeof plong long = 8
所有类型的指针变量在 (32) 位环境中占用四字节,在 (64) 位环境中占用 (8) 字节。
我的编译器是 (x64)( (64) 位)的,如果是 (x86) 编译器( (32) 位)下输出结果应该是 (4) 。
sizeof 与 数组
#include<bits/stdc++.h>
using namespace std;
int main() {
char char_arr[10]="233";
double double_arr[10]={0.0};
int int_arr[10]={0,1,2,3,4,5,6,7,8,9};
long long ll_arr[10];
printf("sizeof arrchar = %d
",sizeof(char_arr));
printf("sizeof arrdouble = %d
",sizeof(double_arr));
printf("sizeof arrint = %d
",sizeof(int_arr));
printf("sizeof arrlong long = %d
",sizeof(ll_arr));
return 0;
}
输出结果:
sizeof arrchar = 10
sizeof arrdouble = 80
sizeof arrint = 40
sizeof arrlong long = 80
sizeof 与结构体
#include<bits/stdc++.h>
using namespace std;
int main() {
struct Student {
char name[20];
bool sex;
int age;
int num;
}stu;
printf("sizeof (Student)stu = %d
",sizeof(stu));
return 0;
}
输出结果:
sizeof (Student)stu = 32
name数组占用 (1*10=20) 个字节,两个int占用 (4*2=8) 个字节,一个bool占用 (1) 个字节,加起来应该是 (29) 。
而程序输出了 (32) ,是不是编译器出问题了?
其实这是正常的,因为结构体的成员变量占用的是一块连续的内存,但是为了保证对不同类型的变量寻址正确,编译器会在储存各变量地址时自动对齐,而不是每个变量的内存块都紧密相连。
计算结构体大小需要了解一个名词偏移量,即结构体变量中成员的地址与结构体首地址的差(首个成员的地址),结构体内容每个成员(包括成员变量、函数、嵌套体)都拥有这一属性。
结构体内偏移量的计算公式为:上一个成员的偏移量 + 上一个成员的占用字节数
为了做到成员地址的对齐,编译器在编译程序时会遵照如下规则:
结构体变量中成员的偏移量必须是该成员大小的整数倍,否则向上补齐
还是用上面的例子,
(name[20]) 为首元素,偏移量为 (0) 。
(sex) 偏移量为 (0 + 20 = 20),(20 \% 1 = 0) ,无须补齐。
(age) 偏移量为 (20 + 1 = 21),(21 \% 4 ≠ 0),偏移量向上补齐为整除 (4) 的 (24)。
(num) 偏移量为 (24 + 4 = 28),(28 \% 4 = 0) ,无须补齐。
最后算出该结构体占用的内存大小为 = num的偏移量 + num占用的大小 = (32) 字节。
根据结构体变量地址对齐的这一特性,还可以知道在结构体中,结构体成员变量写的顺序会影响该结构体占用的空间大小。
根据简单的数学知识,把内存占用较小的结构体成员写在前面是比较优秀的。
此外,结构体标准对齐值也可以自定义,具体操作可以参考这篇博客。
最后,下次写 (memset) 的参数就可以不用写算出占用的空间而不用 (sizeof) 啦!(丝毫没用)