C/C++ 基础之 sizeof 使用(二)

简介: C/C++ 基础之 sizeof 使用

4 结构体类型的 sizeof


对于 struct 数据结构由 CPU 的对齐问题导致 struct 的大小变得比较复杂。具体可以查看以前的文章一文轻松理解内存对齐。


理论上,int 占 4byte , char 占一个 byte ,那么将它们放到一个结构体中应该占 4+1=5byte ;但是实际上,通过运行程序得到的结果是 8byte 。


#include<stdio.h>
struct{
    int x;
    char y;
}Test;
int main()
{
    printf("%d\n",sizeof(Test)); // 输出8不是5
    return 0;
}

结构体的大小跟结构体成员对齐有密切关系,而并非简单地等于各个成员的大小之和!比如对如下结构体两个结构体 A、B 使用 sizeof 的结果分别是:16,24。可以看出 sizeof(B) 并不等于 sizeof(int)+sizeof(double)+sizeof(int)=16 。


struct A
{
  int num1;
  int num2;
  double num3;
};
struct B
{
  int num1;
  double num3;
  int num2;
};

结构体A和B中包含的成员都一样,只不过顺序不同而已,为什么其大小不一样呢?要解释这个问题,就要了解结构体成员对齐的规则。


结构体的大小等于结构体内最大成员大小的整数倍


结构体内的成员的首地址相对于结构体首地址的偏移量是其类型大小的整数倍,比如说 double 型成员相对于结构体的首地址的地址偏移量应该是 8 的倍数。


为了满足规则 1 和 2 编译器会在结构体成员之后进行字节填充!


从三个规则我们来看看为什么 sizeof(B) 等于 24 :首先假设结构体的首地址为0,第一个成员 num1 的首地址是 0 (满足规则2),它的类型是 int ,因此它占用地址空间 0——3 。第二个成员 num3 是 double 类型,它占用 8 个字节,由于之前的 num1 只占用了 4 个字节,为了满足规则 2 ,需要使用规则 3 在 num1 后面填充 4 个字节(4——7),使得 num3 的起始地址偏移量为 8 ,因此 num3 占用的地址空间是:8——15。第三个成员 num2 是 int 型,其大小为 4 ,由于 num1 和num3 一共占用了 16 个字节,此时无须任何填充就能满足规则 2。因此 num2 占用的地址空间是 16——19 。那么是不是结构体的总大小就是 0——19 共 20 个字节呢?请注意,别忘了规则1!由于结构体内最大成员是 double 占用 8 个字节,因此最后还需要在 num2 后面填充 4 个字节,使得结构体总体大小为 24 。


struct S{ };

sizeof(S); // 结果为1

对于一个空 struct 结构体取 sizeof 运算,运算结果为 1 并非 0 。因为编译器为保证此空 struct 存在,专门分配一个字节。


如果存在结构体嵌套,无论内层还是外层均需要采用内存对齐。


5 类的 sizeof


不含继承和 static 成员变量的类。


在这种情况下,只需要考虑对齐方式即可。


class A 
{ 
  public: 
  int b; 
  float c; 
  char d; 
};
class B
{ 
};
int main(void) 
{ 
  cout << “sizeof(A) is ” << sizeof(A) << endl; 
  //输出结果为12
  cout << “sizeof(B) is ” << sizeof(B) << endl; 
  //输出结果为1
  return 0 ; 
}

空的 class 同样也占用 1 个字节。


计算类对象的大小时,类成员函数不占用对象空间,只需要考虑类中数据成员的大小。2类中存在静态成员变量


2.类中存在静态成员变量


class A

{

 public:

 static int a;

 int b;

 float c;

 char d;

};

int main()

{

 A object;

 cout << “sizeof(object) is ” << sizeof(object) << endl;

 //输出结果为12

 return 0 ;

}

因为在程序编译期间,就已经为 static 变量在静态存储区域分配了内存空间,并且这块内存在程序的整个运行期间都存在。而每次声明了类 A 的一个对象的时候,为该对象在堆上,根据对象的大小分配内存。


3.类中包含成员函数


class A 
{ 
  public: 
  static int a; 
  int b; 
  float c; 
  char d; 
  int add(int x,int y) 
  { 
    return x+y; 
  } 
};
int main() 
{ 
  A object; 
  cout << “sizeof(object) is ” << sizeof(object) << endl; 
  b = object.add(3,4); 
  cout << “sizeof(object) is ” << sizeof(object) << endl; 
  //输出结果为12
  return 0 ; 
}

因为只有非静态类成员变量在新生成一个object的时候才需要自己的副本。所以每个非静态成员变量在生成新object需要内存,而function是不需要的。


3 sizeof 与 strlen 区别


sizeof 是一个操作符,strlen 是库函数。


sizeof 的参数可以是数据的类型,也可以是变量,而 strlen 只能以结尾


编译器在编译时就计算出了 sizeof 的结果,而 strlen 函数必须在运行时才能计算出来。并且 sizeof 计算的是数据类型占内存的大小,而 strlen 计算的是字符串实际的长度。


数组做 sizeof 的参数不退化,传递给 strlen 就退化为指针了。如:


int ss[20]="0123456789";
 sizeof(ss)=80, //ss表示在内存中的大小,20*4。
 strlen(ss)    //错误,strlen的参数只能是char*,且必须是以“\0”结尾的。
 char *ss="0123456789";
 sizeof(ss)=4,  //ss是指向字符串常量的字符指针。
 sizeof(*ss)=1, // *ss是第一个字符。

参考资料


https://www.cnblogs.com/Western-Trail/p/10326180.html


《C/C++实践进阶之道》

相关文章
|
6月前
|
C++
C/C++】如何不使用 sizeof 求数据类型占用的字节数
C/C++】如何不使用 sizeof 求数据类型占用的字节数
|
6月前
|
C语言 C++
C++ sizeof()运算符的参数为指针和数组的值为什么不同
C++或C语言中,都可以使用sizeof()运算符来计算数组的字节大小,除此之外,在C++和C语言中,都可以使用一个指向数组第一个元素的内存地址的指针来引用数组,因此,如果要计算数组的字节大小,或长度,传递数组本身或传递指向数组的指针给sizeof()运算符似乎都是可以的,实际上则不然,
72 5
|
6月前
|
Linux C++
(C++)VS下sizeof(string(““))与linux-g++下sizeof(string(““))大小区别及原因剖析
(C++)VS下sizeof(string(““))与linux-g++下sizeof(string(““))大小区别及原因剖析
63 0
(C++)VS下sizeof(string(““))与linux-g++下sizeof(string(““))大小区别及原因剖析
|
6月前
|
安全
来自c++primer5的sizeof运算符
来自c++primer5的sizeof运算符
37 0
|
存储 编译器 C++
C++ sizeof 的介绍
C++ sizeof 的介绍
113 0
|
C++
如何在不使用 sizeof 的情况下在 C/C++ 中找到数组的大小?
我们可以使用 sizeof 运算符找到数组的大小
155 0
|
Linux C++
嵌入式linux基础:c++(五)构造函数
嵌入式linux基础:c++(五)构造函数
160 0
嵌入式linux基础:c++(五)构造函数
|
Linux 编译器 C++
嵌入式linux基础:c++(四)重载 指针 引用
嵌入式linux基础:c++(四)重载 指针 引用
145 0
嵌入式linux基础:c++(四)重载 指针 引用
|
Linux C++
嵌入式linux基础:c++(三)程序结构
嵌入式linux基础:c++(三)程序结构
163 0
嵌入式linux基础:c++(三)程序结构
|
编译器 C语言 C++
C/C++ 中的 sizeof 运算符和 size_t 类型
C/C++ 中的 sizeof 运算符和 size_t 类型