深入理解 C/C++ sizeof() 运算符

过去有一段时间一直以为带个括号的 \(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\n",sizeof(char_var));
	printf("sizeof float = %d\n",sizeof(float_var));
	printf("sizeof double = %d\n",sizeof(double_var));
	printf("sizeof long double = %d\n",sizeof(ldouble_var));
	printf("sizeof int = %d\n",sizeof(int_var));
	printf("sizeof long long = %d\n",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\n",sizeof(char_point));
	printf("sizeof pdouble = %d\n",sizeof(double_point));
	printf("sizeof pint = %d\n",sizeof(int_point));
	printf("sizeof plong long = %d\n",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\n",sizeof(char_arr));
	printf("sizeof arrdouble = %d\n",sizeof(double_arr));
	printf("sizeof arrint = %d\n",sizeof(int_arr));
	printf("sizeof arrlong long = %d\n",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\n",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\) 啦!(丝毫没用)

posted @ 2020-02-11 14:11  暖暖草果  阅读(358)  评论(0编辑  收藏  举报