sizeof运算符

sizeof是C/C++的编译时操作符,用于获取变量或类型的字节大小。它不会忽略括号内的运算,能用于表达式、类型、函数返回值,但不能用于函数名、不确定类型或位字段。sizeof对结构体、数组、引用等有特殊处理,考虑内存对齐和静态成员。在计算数组大小时,sizeof包含'',strlen()则不包括。对于联合体,sizeof返回最大成员的大小。枚举类型的sizeof通常等于int的大小。注意,sizeof在不同系统和编译器下可能有所不同,如32位和64位系统的指针大小。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Sizeof的说明

sizeofC/C++语言的一种单目操作符,它不是函数。sizeof操作符返回值为其操作数的存储大小,用字节数表示。操作数可以是一个表达式或括在括号内的类型名。 sizeof的结果是size_t,它被定义为unsigned int类型。该类型保证容纳显示所建立的最大对象的字节大小。

需牢记sizeof的计算发生在编译时刻,所以它可以被当作常量表达式使用,且会忽略其括号内的各种运算。

二、Sizeof的使用

 1、sizeof的使用形式:sizeof(var_name)或者sizeof var_name 

 2、数据类型必须用括号括起来:sizeof(type)

变量、对象可以不用括号括住:sizeof a,但若是类型必须用括号。

 3、sizeof可以对表达式求值:sizeof(2.5+3.14),实际是sizeof(double)

 4、sizeof可以对函数调用求值,实际上是对返回值类型求值

 int func(char s[5]);
 {
 cout<<endl;
 return 1;
 }
sizeof(func(“1234”))=4 //因为func的返回类型为int,所以相当于求sizeof(int)=4,sizeof(s)=4

 5、以下情况不能用sizeof进行求值

1)不能对函数名求值

2)不能对不确定返回值的类型求值,如void

3)位域成员不可以使用sizeof求值

4)不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。 

  一般,在32位编译器下:

       sizeof(char):1     

     sizeof(short):2

          sizeof(int):4

     sizeof(long):4

       sizeof(float):4

       sizeof(double):8

       sizeof(long long):8

     sizeof(p):4,(p为指针)

32位系统下指针的sizeof是4字节,64位下是8字节

6.指针与静态数组的sizeof操作
      1) 指针均可看为变量类型的一种。所有指针变量的sizeof 操作结果均为4。
        注意: int *p; sizeof(p)=4;(32位系统下始终不变)sizeof(*p)相当于sizeof(int),随着数据类型的改变而改变;      
      2) 对于静态数组,sizeof可直接计算数组大小;当数组动态分配时,int *a = new int[10]; sizeof = 4。
         例: int a[10];char b[]="hello";
                sizeof(a)等于4*10=40;
                sizeof(b)等于6;
          
注意数组做形参时,数组名称当作指针使用!!
               void  fun(char p[]) {sizeof(p)等于4}
如下程序:
#include <stdio.h>
void func(char p[])
{
    printf("in func args,p[] size %d\n", sizeof(p));
}
int main()
{
    int *p;
    int a[10];
    char b[] = "hello";

    printf("*p size %d\n", sizeof(p));
    printf("a[10] size %d\n", sizeof(a));
    printf("\"hello\" size %d\n", sizeof(b));
    func(b);
    return 0;
}
输出结果:*p size 4
               a[10] size 40
               "hello" size 6
               in func args,p[] size 4
经典问题: 
//double* (*a)[3][6]; 
#include <stdio.h>
int main()
{
    double *(*a)[3][6];
    printf("        a size %4d\n",sizeof(      a));// 4 a为指针
    printf("      *a size %4d\n",sizeof(    *a));// 72 *a为一个有3*6个指针元素的数组
    printf("    **a size %4d\n",sizeof(   **a));// 24 **a为数组一维的6个指针
    printf("  ***a size %4d\n",sizeof(  ***a));// 4 ***a为一维的第一个指针
    printf("****a size %4d\n",sizeof(****a));// 8 ****a为一个double变量
    return 0;
}
 问题解析:a是一个很奇怪的定义,他表示一个指向double*[3][6]类型数组的指针。既然是指针,所以sizeof(a)就是4 
                
既然a是执行double*[3][6]类型的指针,*a就表示一个double*[3][6]的多维数组类型,因此sizeof(*a)=3*6*sizeof(double*)=72
       同样的,
**a表示一个double*[6]类型的数组,所以sizeof(**a)=6*sizeof  (double*)=24
      
***a就表示其中的一个元素,也就是double*了,所以sizeof(***a)=4
       至于
****a,就是一个double了,所以sizeof(****a)=sizeof(double)=8 

7、sizeof与strlen()比较,在计算字符数组大小时,sizeof包含'\0',strlen()不包含'\0',strlen()遇到’\0’就会返回

char c1[ ]={‘c’,‘ ’,‘p’,‘r’,‘o','g','r','a','m'};
char c2[ ]={‘c’,‘ ’,‘p’,‘r’,‘o','g','r','a','m','\0'};

strlen(c1)未定义,strlen(c2)=9;

sizeof(c1)=9,sizeof(c2)=10

注意,sizeof("\0")=2

8、对引用的sizeof,其实是对其所绑定的对象的sizeof

9、对于C字符串,需要牢记C/C++中一个汉字占两个字节(Linux下3个字节),且字符串尾有空字符

使用伪指令#pragma pack(n),编译器将按照n个字节对齐;

使用伪指令#pragma pack(n),将取消自定字节对齐方式。

10、 sizeof的参数为结构或类时,有两点需要注意:

第一、结构或者类中的静态成员不对结构或者类的大小产生影响,因为静态变量的存储位置与结构或者类的实例地址无关。

第二、没有成员变量的结构或类的大小为1,因为必须保证结构或类的每一个实例在内存中都有唯一的地址。

还有结构体或者类中有虚函数的,大小会加4个字节,因为有虚函数表的指针。

Class Test{int a;static doublec};//sizeof(Test)=4.
Test *s;//sizeof(s)=4,s为一个指针。

Class test1{ };//sizeof(test1)=1;

测试Demo:

#include<iostream>
#include<cstdlib>
#include<string>
using namespace std;
struct s
{
         charc;
          char b;
         int  a;
         doubled;
};
int fun(int);
int main()
{
  int  a=3;
  s s1;
  cout<<sizeof s1<<endl;
  cout<<sizeof(s1)<<endl;
  cout<<sizeof a<<endl;
  cout<<sizeof(a)<<endl;
  cout<<sizeof(int)<<endl;
  cout<<sizeof(2+3.4)<<endl;  //sizeof(double)
  cout<<sizeof 2+3.4<<endl;  //sizeof(double)
  cout<<sizeof(fun(a))<<endl;
  cout<<sizeof fun(a)<<endl;
  cout<<sizeof 3<<endl;
  cout<<sizeof(3)<<endl;
 system("pause");
 return 0;
}
int fun(int a)
{
         return a; 
}
 

三、sizeof的注意项:

1、联合类型操作数的sizeof是其最大字节成员的字节数。

2、结构体(内存对齐): 

  struct {char b; double x;} a; 

  在32位机器上sizeof(a)=12,而一般sizeof(char)+ sizeof(double)=9。因为编译器在考虑对齐问题时,在结构中插入空位以控制各成员对象的地址对齐。如double类型的结构成员x要放在被4整除的地址。 

3、如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。 

四、sizeof的主要用途

  1、sizeof操作符的一个主要用途是与存储分配和I/O系统那样的例程进行通信。例如: 

  void *malloc(size_t size), 

  size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream)。 

  2、sizeof的另一个的主要用途是计算数组中元素的个数。例如: 

  void * memset(void * s,int c,sizeof(s))。 

由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用sizeof来代替常量计算。

五、 sizeof计算结构体大小:

为了提高CPU的存储速度,编译器会对一些变量的起始地址做对齐处理。在默认情况下,规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。

各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节编译器会自动填充。同时为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。(通常是取占内存最大的变量类型的大小的整倍数)

总体上遵循两个原则:

1)整体空间是占用空间最大的成员(的类型)所占字节数的整倍数。但在linux+gcc环境下,若最大的成员(的类型)所占字节数超过4,则整体空间是4的倍数。

 2)数据对齐原则----内存按结构成员的先后顺序排列,当排到该成员变量时,其前面已摆放的空间大小必须是该成员类型大小的整倍数,如果不够则补齐,以此向后类推,但在linux+gcc环境下,若最大的成员(的类型)所占字节数超过4,则前面已摆放的空间大小是4的整倍数

注意:数组按照单个变量一个一个的摆放,而不是看成整体。如果成员中有自定义的类、结构体,也要注意数组问题。

因为对齐问题使结构体的sizeof变得比较复杂,看下面的例子:(默认对齐方式下)

struct s1
{
    char a;
    double b;
    int c;
    char d;
};
struct s2
{
    char a;
    char b;
    int c;
    double d;
};
cout<<sizeof(s1)<<endl; // 24
cout<<sizeof(s2)<<endl; // 16

同样是两个char类型,一个int类型,一个double类型,但是因为对齐问题,导致他们的大小不同。计算结构体大小可以采用元素摆放法,我举例子说明一下:首先,CPU判断结构体的对界,根据上一节的结论,s1s2的对界都取最大的元素类型,也就是double类型的对界8。然后开始摆放每个元素。

       对于s1,首先把a放到8的对界,假定是0,此时下一个空闲的地址是1,但是下一个元素ddouble类型,要放到8的对界上,离1最接近的地址是8了,所以d被放在了8,此时下一个空闲地址变成了16,下一个元素c的对界是416可以满足,所以c放在了16,此时下一个空闲地址变成了20,下一个元素d需要对界1,也正好落在对界上,所以d放在了20,结构体在地址21处结束。由于s1的大小需要是8的倍数,所以21-23的空间被保留,s1的大小变成了24

       对于s2,首先把a放到8的对界,假定是0,此时下一个空闲地址是1,下一个元素的对界也是1,所以b摆放在1,下一个空闲地址变成了2;下一个元素c的对界是4,所以取离2最近的地址4摆放c,下一个空闲地址变成了8,下一个元素d的对界是8,所以d摆放在8,所有元素摆放完毕,结构体在15处结束,占用总空间为16,正好是8的倍数。

1.含结构体的结构体的空间计算

struct s3
{
    char c;
    int i;
};
struct s4
{
    char c1;
	s3 s;
    char c2;
};
cout<<sizeof(s3)<<endl; // 8
cout<<sizeof(s4)<<endl; // 16

修改两个原则如下:

1)整体空间是子结构体与父结构体中占用空间最大的成员(的类型)所占字节数的整倍数。但在linux+gcc环境下,若最大的成员(的类型)所占字节数超过4,则整体空间是4的倍数。

 2)数据对齐原则----父结构体中内存按结构成员的先后顺序排列,当排到子结构体时,其前面已摆放的空间大小必须是子结构体成员中类型大小的整倍数,如果不够则补齐,以此向后类推,但在linux+gcc环境下,若最大的成员(的类型)所占字节数超过4,则前面已摆放的空间大小是4的整倍数。

2.含数组的结构体的空间计算

struct s1
{
    char a[8];//s1的大小是char的整数倍即可
};
    struct s2
{
    double d;
};
struct s3
{
    s1 s;
    char a;
};
struct s4
{
    s2 s;
    char a;
};
cout<<sizeof(s1)<<endl; // 8
cout<<sizeof(s2)<<endl; // 8
cout<<sizeof(s3)<<endl; // 9
cout<<sizeof(s4)<<endl; // 16;

s1s2大小虽然都是8,但是s1的对齐方式是1s28double),所以在s3s4中才有这样的差异。

所以,在自己定义结构体的时候,如果空间紧张的话,最好考虑对齐因素来排列结构体里的元素。

3.含位域的结构体的空间计算

在结构体和类中,可以使用位域来规定某个成员所能占用的空间,所以使用位域能在一定程度上节省结构体占用的空间。不过考虑下面的代码:

struct s1
{
    int i: 8;
    int j: 4;
    double b;
    int a: 3;
};
struct s2
{
    int i;
    int j;
    double b;
    int a;
};
struct s3
{
    int i;
    int j;
    int a;
    double b;
};
struct s4
{
    int i: 8;
    int j: 4;
    int a: 3;
    double b;
};
cout<<sizeof(s1)<<endl; // 24
cout<<sizeof(s2)<<endl; // 24
cout<<sizeof(s3)<<endl; // 24
cout<<sizeof(s4)<<endl; // 16

可以看到,有double存在会干涉到位域(sizeof的算法参考上一节),所以使用位域的的时候,最好把float类型和double类型放在程序的开始或者最后。

4.使用"#pragma pack(n)"结构体的空间计算

C/C++中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。下面举例说明其用法。

#pragma pack(push) //保存对齐状态
#pragma pack(4)//设定为4字节对齐
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop)//恢复对齐状态

以上结构的大小为16,设定的对齐方式(4字节对齐),m1占用1个字节。接着开始为m4分配空间,这时其偏移量为1,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于n),m4占用8个字节。接着为m3分配空间,这时其偏移量为12,满足为4的倍数,m3占用4个字节。这时已经为所有成员变量分配了空间,共分配了16个字节,满足为n的倍数。

5.空结构体的大小为1

六、 sizeof计算union大小:

整个联合体的sizeof也就是联合体成员中的最大值,也要考虑对齐问题。

七、 sizeof计算枚举大小:

枚举只是定义了一个常量集合,里面没元素,而枚举类型是当做int类型存储的,故枚举类型的sizeof值都为4.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值