c语言sizeof基础知识,C语言的基本知识-sizeof和offsetof

20190216090314833531.png

1,定义大小

sizeof是C / C ++中的一个运算符. 简而言之,其功能是返回对象或类型占用的内存字节数. (关于MSDN的解释是: sizeof关键字以字节为单位给出与变量或类型(包括聚合类型)关联的存储量. 此关键字返回size_t类型的值)

返回值类型size_t取决于编译系统的值,通常在头文件stddef.h中定义为typedef unsigned int size_t;

世界上有很多编译器,但是作为一个规范,它们将确保char,signed char和unsigned char的sizeof值为1,毕竟char是我们可用于编程的最小数据类型

2. 语法

实际上,sizeof计算对象的大小也转换为对象类型的计算,也就是说,相同类型的不同对象的sizeof值是相同的. 在这里,对象可以进一步扩展到表达式,即sizeof可以评估表达式,编译器根据表达式的最终结果类型确定大小,并且通常不计算表达式. 如:

sizeof(2); // 2的类型是int,所以它等同于sizeof(int);

sizeof(2 + 3.14); // 3.14的类型为double类型,2也将被提升为double类型,因此它等效于sizeof(double);

sizeof还可以评估函数调用,结果是函数返回类型的大小,该函数不会被调用,让我们看一个完整的示例:

char foo()

{

printf(“ foo()已被调用. \ n”);

返回'a';

}

int main()

{

size_t sz = sizeof(foo()); // foo()的返回类型为char,因此sz = sizeof(char),foo()不会被调用

printf(“ sizeof(foo())=%d \ n”,sz);

返回0;

}

C99标准规定不能使用sizeof值来计算函数,不确定类型的表达式和位字段成员,也就是说,以下编写方法是错误的:

sizeof(foo); //错误

void foo2(){}

sizeof(foo2()); //错误

结构S

{

unsigned int f1: 1;

unsigned int f2: 5;

unsigned int f3: 12;

};

sizeof(S.f1); //错误

3. sizeof的常数

sizeof的计算在编译时进行,因此可以用作常量表达式,例如:

269d3978ce278cda67c92d5523fcf97c.png

charary [sizeof(int)* 10]; //好的

最新的C99标准规定sizeof也可以在运行时计算. 例如,以下程序可以在Dev-C ++中正确执行:

int n;

n = 10; // n个动态分配

基元[n]; // C99还支持数组的动态定义

printf(“%d \ n”,sizeof(ary)); // 好. 输出10

但是它不能在不能完全实现C99标准的编译器中工作. 上面的代码无法在VC6中编译. 因此,我们最好认为sizeof是在编译时执行的,这样就不会带来错误并使程序更易于移植.

4. 基本数据类型的大小

此处的基本数据类型是指简单的内置数据类型,例如short,int,long,float和double. 由于它们取决于系统,因此在不同的系统下值可能会有所不同. 这必须引起我们的注意,在这方面尽量不要麻烦移植您自己的程序. 通常,在32位编译环境中,sizeof(int)的值为4.

5. sizeof指针变量

您应该知道,在研究数据结构之后,指针是一个非常重要的概念. 它记录另一个对象的地址. 由于它用于存储地址,因此它当然等于计算机内部地址总线的宽度. 因此,在32位计算机中,指针变量的返回值必须为4(请注意结果以字节为单位),而在64位系统中指针变量的结果大小为8.

指针变量的sizeof值与指针所指向的对象无关,并且所有指针变量都占用相同的内存量.

6. sizeof数组

数组的sizeof值等于数组占用的内存字节数,例如:

char a1 [] =“ abc”;

int a2 [3];

sizeof(a1); //结果为4,字符末尾有一个NULL终止符

sizeof(a2); //结果为3 * 4 = 12(取决于int)

一些朋友在开始时使用sizeof作为数组元素的数量. 现在,您应该知道这是错误的. 那么如何找到数组元素的数量呢?通常有两种书写方式:

int c1 = sizeof(a1)/ sizeof(char); //总长度/单个元素的长度

int c2 = sizeof(a1)/ sizeof(a1 [0]); //总长度/第一个元素的长度

在这里写,请问,c3和的以下值应该是什么?

void foo3(字符a3 [3])

{

int c3 = sizeof(a3); // c3 ==

}

void foo4(字符a4 [])

{

int = sizeof(a4); // == 1

}

也许当您尝试回答的值时,您已经意识到c3是错误的,是的,c3! =3. 在这里,函数参数a3不再是数组类型,而是指针,等效于char * a3. 为什么不难理解为什么要考虑,当我们调用函数foo3时,程序将在堆栈上分配大小为3的数组?将不会!该数组是“按地址传递”的,调用方只需传递实际参数的地址,因此a3自然是指针类型(char *),而c3的值为4.

7. 结构尺寸

结构S1

37d7a3c6a8fa9cc1bb4d21e5bb3f180c.png

{

字符c;

int i;

};

sizeof(s1)结果不符合预期5.

应用于结构类型或变量时,sizeof返回实际大小,其中可能包括插入的填充字节以进行对齐.

原来,这是传奇的字节对齐!一个重要的话题出现了. 为什么需要字节对齐计算机组成原理来告诉我们,这有助于加快计算机的访问速度,否则将花费更多的指令周期. 因此,编译器将默认处理结构(实际上,其他地方的数据变量也是如此),以便宽度2的基本数据类型(short等)位于可被2整除的地址处, width为4. 基本数据类型(int等)位于可被4整除的地址上,依此类推. 这样,可能需要在两个数字之间添加填充字节sizeof计算结构体大小,因此整个结构的sizeof值会增加.

让我们交换char和int在S1中的位置:

结构S2

{

int i;

字符c;

};

看一下sizeof(S2)的结果,为什么仍然是8,然后再看内存,原始成员c后面仍然有3个填充

收费字节,为什么?不用担心,让我们总结以下规则.

字节对齐的详细信息与编译器的实现有关sizeof计算结构体大小,但通常满足三个条件:

对于上述准则,有几点需要解释:

1)我并不是说结构成员的地址是其大小的整数倍. 我们为什么要谈论偏移量?因为存在第一点,所以我们只能考虑成员的偏移量. 考虑起来很简单. 考虑为什么.

结构的成员相对于结构的第一个地址的偏移量可以通过宏offsetof()获得,该宏也在stddef.h中定义,如下所示:

#define offsetof(s,m)(size_t)&(((s *)0)-> m)

例如,如果要在S2中获取c的偏移量,则方法为

size_t pos = offsetof(S2,c); // pos等于4

2)基本类型是指上述内置数据类型,例如char,short,int,float,double等. 此处提到的“数据宽度”是指其sizeof的大小. 因为结构的成员可以是复合类型,例如另一个结构,所以在查找最宽的基本类型成员时,应包括复合类型成员的子成员,而不是将复合成员作为一个整体来对待. 但是,在确定复合类型的构件的偏移位置时,将复合类型视为一个整体. 这里的描述有点草率,思考时有点草率,让我们看一下示例(具体值仍为VC6作为示例,以后将不再描述):

结构S3

{

char c1;

S1 s;

字符c2

};

S1的最宽简单成员的类型为int,而S3在考虑最宽的简单成员时认为S1被分解,因此S3的最宽简单类型为int,因此由S3定义的变量其存储空间的地址需要除以4,整个sizeof(S3)的值也应除以4. c1的偏移量为0,而s的偏移量现在为一个整数. 作为结构变量,它还满足前三个条件,因此其大小为8,偏移量为4,在c1和s之间需要3个填充字节,而在c2和s之间不需要,因此c2的偏移量为12,c2的大小为13、13不能除以4,因此必须在末尾添加3个填充字节. 最后,sizeof(S3)的值为16.

通过上面的描述,我们可以获得一个公式:

结构的大小等于最后一个成员的偏移量加上其大小加上末尾的填充字节数,即:

sizeof(结构)= offsetof(最后一项)+ sizeof(最后一项)+ sizeof(尾随填充)

95d8fc184feae80c8ba0e11c4f3a07e3.png

在这一点上,朋友应该对结构的大小有了新的了解,但不要太高兴. 尚未提及影响sizeof的重要参数. 那就是编译器的打包指令. 它用于调整结构的对齐方式. 不同编译器的名称和用法略有不同. 在VC6中,它是通过#pragma pack实现的. 您也可以直接修改/ Zp编译开关. #pragma pack的基本用法是: #pragma pack(n),n是字节对齐

Number,值是1、2、4、8、16,默认值是8,如果此值小于结构成员的sizeof值,则成员的偏移量应基于此值,也就是说,结构构件的偏移量应取两者中的最小值,公式如下:

offsetof(item)= min(n,sizeof(item))

再次查看示例:

#pragma pack(push)//将当前包设置保存在堆栈中

#pragma pack(2)//必须在结构定义之前使用

结构S1

{

字符c;

int i;

};

结构S3

{

char c1;

S1 s;

字符c2

};

#pragma pack(pop)//恢复以前的包设置

在计算sizeof(S1)时,min(2,sizeof(i))的值为2,因此i的偏移量为2,加上sizeof(i)等于6,可以除以2,因此整个S1的大小为6. 类似地,对于sizeof(S3),s的偏移量为2,c2的偏移量为8,加上sizeof(c2)等于9,不能除以2,添加一个填充字节,因此sizeof(S3)等于10.

还必须注意,“空结构”(没有数据成员)的大小不是0,而是1. 想象一下如何解决“空位”变量以及两个不同的“空结构”变量可以区分. 因此,还必须存储“空结构”变量,以便编译器只能为空间占用分配一个字节的空间. 如下:

struct S5 {};

sizeof(S5); //结果为1

8. 包含位域结构的sizeof

正如我之前所说,不能将位字段的成员单独视为sizeof值. 我们在这里要讨论的是包含位字段的结构的sizeof. 仅出于考虑其特殊性而专门列出. C99规定可以将int,unsigned int和bool用作位字段类型,但是几乎所有编译器都对其进行了扩展,以允许存在其他类型.

使用位字段的主要目的是压缩存储,一般规则是:

1)如果相邻位字段的类型相同,并且它们的位宽之和小于该类型的大小,则以下字段将存储在前一个字段的旁边,直到无法容纳它们为止;

2)如果相邻位字段的类型相同,但是它们的位宽之和大于该类型的大小,则以下字段将从新存储单元开始,并且偏移量将是其类型大小的整数倍;

3)如果相邻位字段字段的类型不同,则每个编译器的具体实现也不同. VC6采用非压缩模式,Dev-C ++采用压缩模式;

4)如果非位字段散布在位字段之间,将不执行压缩;

5)整个结构的总大小是最宽的基本类型成员的大小的整数倍.

让我们看一个例子.

示例1:

结构BF1

20180110182854295277.png

{

char f1: 3;

char f2: 4;

char f3: 5;

};

内存布局为:

1112222 ^ 33333

位字段类型为char. 第一个字节只能容纳f1和f2,因此f2被压缩为第一个字节,而f3只能从下一个字节开始. 因此,sizeof(BF1)的结果为2.

示例2:

结构BF2

{

char f1: 3;

短f2: 4;

char f3: 5;

};

由于相邻位字段的类型不同,在VC6中它的sizeof为6,而在Dev-C ++中它的大小为2.

示例3:

结构BF3

{

char f1: 3;

char f2;

char f3: 5;

};

非位字段散布在其中,不会进行压缩,并且在VC6和Dev-C ++中获得的大小为3

9. 工会的规模

该结构在内存组织中是顺序的,并且联合是重叠的. 每个成员共享一部分内存,因此整个联合的sizeof是每个成员的最大大小. 结构的成员也可以是复合类型,其中复合类型成员被视为一个整体.

因此,在以下示例中,U的sizeof值等于sizeof(s).

联盟U

{

int i;

字符c;

S1 s;

};

#include

/ *成员MEMBER在TYPE类型的结构中的偏移量. * /

#define offsetof(TYPE,MEMBER)__builtin_offsetof(TYPE,MEMBER)

本文来自电脑杂谈,转载请注明本文网址:

http://www.pc-fly.com/a/jisuanjixue/article-206014-1.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值