C语言:关键字---sizeof(计算结构体的内存)

sizeof是一个“披着函数皮的关键字”,是一种单目操作符,绝对不是函数。


一、sizeof 简介

1. 一般形式

sizeof(对象);
sizeof(object);

注意:除了数据类型,其他的对象可以不用括号,这也是sizeof不是函数最有力的证明。

2. 功能

返回一个对象在内存中所占中的存储空间,以字节为单位计数。
对象包括:变量、基本数据类型、数组、指针和结构体等。

二、sizeof 用法

1. 基本数据类型

数据类型 数据类型符 占用字节数 数值范围
整型 int 2/4 同短整型/长整型
短整型 short 2 -32768~32767
长整形 long 4 -2的31次方~2的31次方-1
单精度浮点型 float 4 -10的38次方~10的38次方
双精度浮点型 double 8 -10的308次方~10的308次方
字符型 char 1 -128~127

注意:整型int的字节数由系统决定的,在16位系统中占2个字节,在32位系统中占4个字节
具体代码如下:

#include <stdio.h>

int main(void)
{
    
    
	printf("int=%d,short=%d,long=%d,float=%d,double=%d,char=%d\n",
	sizeof(int),sizeof(short),sizeof(long),sizeof(float),sizeof(double),sizeof(char));
	
	return 0;
}

运行代码如下:

int=4,short=2,long=4,float=4,double=8,char=1

2. 变量

由基本数据类型定义的变量,所占用的内存空间,与其基本类型的占用内存一样。
具体代码如下:

#include <stdio.h>

	int 	a;
	short 	b;
	long  	c;
 	float	d;
 	double  e;
 	char    f;

int main(void)
{
    
    
	printf("a=%d,b=%d,c=%d,d=%d,e=%d,f=%d\n",
	sizeof(a), sizeof(b), sizeof(c),sizeof(d),sizeof(e),sizeof(f));
	
	return 0;
}

运行结果如下:

a=4,b=2,c=4,d=4,e=8,f=1

3. 指针

指针是一个占据存储空间的实体在这一段空间起始位置的相对距离值。在C/C++语言中,指针一般被认为是指针变量,指针变量的内容存储的是其指向的对象的首地址,指向的对象可以是变量(指针变量也是变量),数组,函数等占据存储空间的实体。
指针变量是用来存放内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的,也就是说所有数据的存放地址的长度是相同的,由计算机的位数决定。
通常情况下,指针变量的sizeof返回值:32位系统—4字节,64位系统—8字节。
具体代码如下:

#include <stdio.h>

	int 	*a={
    
    1,2,3,4,5};
	short 	*b={
    
    1,2,3,4,5};
	long  	*c={
    
    1,2,3,4,5};
 	float	*d={
    
    1,2,3,4,5};
 	double  *e={
    
    1,2,3,4,5};
 	char    *f="12345";
 	void(*p);	//参数指针 

int main(void)
{
    
    
	printf("a=%d,b=%d,c=%d,d=%d,e=%d,f=%d,p=%d\n",
	sizeof(a), sizeof(b), sizeof(c),sizeof(d),sizeof(e),sizeof(f),sizeof(p));
	return 0;
}

运行结果如下:

a=4,b=4,c=4,d=4,e=4,f=4,p=4

4. 数组

数组的sizeof值等于数组所占用的内存字节数,并不是数组元素的个数。
具体代码如下:

#include <stdio.h>

	int 	a[]={
    
    1,2,3,4,5};
	short 	b[]={
    
    1,2,3,4,5};
	long  	c[]={
    
    1,2,3,4,5};
 	float	d[]={
    
    1,2,3,4,5};
 	double  e[]={
    
    1,2,3,4,5};
 	char    f[]="12345";

int main(void)
{
    
    
	printf("a=%d,b=%d,c=%d,d=%d,e=%d,f=%d\n",
	sizeof(a), sizeof(b), sizeof(c),sizeof(d),sizeof(e),sizeof(f));
	
	return 0;
}

运行结果如下:

a=20,b=10,c=20,d=20,e=40,f=6

注意:字符串的末尾存在一个NULL终止符,所以会多出有一个字节。

如想得到数组的元素个数,有以下两种方法:

  • 总长度/相对应的数据类型长度
  • 总长度/首元素长度

具体代码如下:

#include <stdio.h>

	int 	a[]={
    
    1,2,3,4,5};
	short 	b[]={
    
    1,2,3,4,5};
	long  	c[]={
    
    1,2,3,4,5};
 	float	d[]={
    
    1,2,3,4,5};
 	double  e[]={
    
    1,2,3,4,5};
 	char    f[]="12345";

int main(void)
{
    
    
	printf("a=%d,b=%d,c=%d,d=%d,e=%d,f=%d\n",
	sizeof(a)/sizeof(int), sizeof(b)/sizeof(short), sizeof(c)/sizeof(long),sizeof(d)/sizeof(float),sizeof(e)/sizeof(double),sizeof(f)/sizeof(char));

	printf("a=%d,b=%d,c=%d,d=%d,e=%d,f=%d\n",
	sizeof(a)/sizeof(a[0]), sizeof(b)/sizeof(b[0]), sizeof(c)/sizeof(c[0]),sizeof(d)/sizeof(d[0]),sizeof(e)/sizeof(e[0]),sizeof(f)/sizeof(f[0]));
	return 0;
}

运行结果如下:

a=5,b=5,c=5,d=5,e=5,f=6
a=5,b=5,c=5,d=5,e=5,f=6

注意:由于字符串的末尾存在一个NULL终止符,会多出有一个字节,计算数组元素个数时,可以减一。

参数数组是一个特殊的存在,由于数组是“传址”的,调用者只需将实参的地址传递过去,所以参数数组自然为指针类型,这样参数数组的sizeof返回值就是指针变量的返回值。
具体代码如下:

#include <stdio.h>

	int 	a[5]={
    
    1,2,3,4,5};
	short 	b[5]={
    
    1,2,3,4,5};
	long  	c[5]={
    
    1,2,3,4,5};
 	float	d[5]={
    
    1,2,3,4,5};
 	double  e[5]={
    
    1,2,3,4,5};
 	char    f[5]="12345";

void MQ1(char data1[5])
{
    
    
	printf("data1=%d\n",sizeof(data1));
} 

void MQ2(char data2[])
{
    
    
	printf("data2=%d\n",sizeof(data2));
} 

int main(void)
{
    
    
	MQ1(f);
	MQ2(f);
	return 0;
}

运行结果如下:

data1=4
data2=4

5. 结构体

结构体的sizeof值等于结构体所占用的内存字节数。
具体代码如下:

#include <stdio.h>

struct MQ
{
    
    
	char    a;
	int 	b;
} data={
    
    'a',0x55555555};

int main(void)
{
    
    
	printf("struct=%d\n",sizeof(data));
	return 0;
}

运行结果:

struct=8

一个char占1个字节,一个int占4个字节,那么加起来就应该是5,为什么运行结果是8?
由于字节对齐的原因,为此编译器默认会对结构体进行处理,让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被4整除的地址上,以此类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。
看一下填充情况,具体运行代码如下:

#include <stdio.h>

struct MQ
{
    
    
	char    a;
	int 	b;
} data={
    
    'a',0x55555555};

int main(void)
{
    
    
	printf("struct=%d\n",sizeof(data));
	
	adder=&data;//获取结构体首地址
	for(i=0;i<sizeof(data);i++)
	printf("%x ",*(unsigned __int8*)(adder+i));
	printf("\n");
	return 0;
}

运行结果如下:

struct=8
61 0 0 0 55 55 55 55

有三个填充字节。

计算方法:主要就是判断什么时候添加填充字节,填充多少填充字节。

  • 以最宽的数据类型长度为单元,因为结构体的总大小为结构体最宽基本类型成员大小的整数倍。
  • 在一个单元内,判断下一个数据的长度与本数据的大小关系:如果下一个数据更宽,就有可能添加填充字节,使其长度一致。
  • 如果加上下一个数据,字节数超过单元长度,就在本数据后添加填充字节,使其单元内部的长度与单元一致,下一个数据另开辟一个单元。
  • 如果一个单元内只剩一个数据,后面添加填充字节,使其长度与单元一致。

具体代码如下:

#include <stdio.h>

struct MQ
{
    
    
	char    a;
	int 	b;
	
	char	c;
	short 	d;
	
	short 	e;
	char	f;
	
	short 	g;
	int  	h;
	
	char    k;
} data={
    
    'a',0x55555555,'b',0x1111,0x2222,'c',0x3333,0x44444444,'d'};//这样定义有利于判断填充字节 

int main(void)
{
    
    
	int i=0,adder=0;
	printf("struct=%d\n",sizeof(data));

	adder=&data;
	for(i=0;i<sizeof(data);i++)
	printf("%x ",*(unsigned __int8*)(adder+i));
	printf("\n");

	return 0;
}

运行结果如下:

struct=28
61 0 0 0 55 55 55 55 62 0 11 11 22 22 63 0 33 33 0 0 44 44 44 44 64 0 0 0

计算思路:
首先判断单元长度,为int的长度4字节。

  1. a是char型,1个字节,b是int型,4个字节,a+b=5>4,所以在a后添加三个0填充字节,这个单元就满了。
  2. b自己一个单元,无需填充。
  3. c=1字节,d=2字节,c+d=3<4,可以组成一个单元。在单元内,c<d,c要与d等长,所以c后添加一个0填充字节,这个单元就满了。
  4. e=2字节,f=1字节,e+f=3<4,可以组成一个单元。在单元内,e>f,e不需要填充。g=2字节,e+f+g=5>4,所以在f后添加一个0填充字节,这个单元就满了。
  5. g进入下一个单元,g=2字节,h=4字节,g+h=6>4,所以在g后添加二个0填充字节,这个单元就满了。
  6. h自己一个单元,无需填充。
  7. 最后一个单元只剩k=1字节,后面添加三个0填充字节,使其长度与单元一致。

一共7个单元,28个字节。


以上就是结构体的sizeof值的计算思路,有些C语言的考试题会遇见,其他情况直接调用程序就可以轻松得到答案。

猜你喜欢

转载自blog.csdn.net/MQ0522/article/details/111522605