C语言中sizeof的用法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言中sizeof的用法相关的知识,希望对你有一定的参考价值。
参考技术A sizeof是C/C++中的一个操作符(operator),作用就是返回一个对象或者类型所占的内存字节数。返回值类型为size_t,在头文件stddef.h中定义这是一个依赖于编译系统的值,一般定义为typedef unsigned int size_t;编译器林林总总,但作为一个规范,都会保证char、signed
char和unsigned char的sizeof值为1,毕竟char是编程能用的最小数据类型。
MSDN上的解释为:
The sizeof keyword gives the amount of storage, in bytes, associated with avariable or a
type (including aggregate types). This keyword returns a value of type
size_t.
2、语法:
sizeof有三种语法形式,如下:
1) sizeof( object ); // sizeof( 对象 );
2) sizeof( type_name ); // sizeof( 类型 );
3) sizeof object; // sizeof 对象;
所以一下三种sizeof的使用都是对的
复制代码 代码如下:
#include
main()
int b;
printf("%dn",sizeof b);
printf("%dn",sizeof(b));
printf("%dn",sizeof(int));
4、基本数据类型的sizeof
这里的基本数据类型指short、int、long、float、double这样的简单内置数据类型,由于它们都是和系
统相关的,所以在不同的系统下取值可能不同,这务必引起我们的注意,尽量不要在
这方面给自己程序的移植造成麻烦。一般的,在32位编译环境中,sizeof(int)的取值为4。
5、指针变量的sizeof
等于计算机内部地址总线的宽度。所以在32位计算机中,一个指针变量的返回值必定是4(注意结果是以
字节为单位),可以预计,在将来的64位系统中指针变量的sizeof结果为8。
指针变量的sizeof值与指针所指的对象没有任何关系,正是由于所有的指针变量所占内存大小相等,所以
MFC消息处理函数使用两个参数WPARAM、LPARAM就能传递各种复杂的消息结构(使用
指向结构体的指针)。
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] ); // 总长度/第一个元素的长度。注意数组名做函数参数传递
时退化为指针。
7、结构体的sizeof
struct S1
char c;
int i;
;
sizeof的结果等于对象或者类型所占的内存字节数,好吧,那就让我们来看看S1的内存分配情况:S1 s1
= 'a', 0xFFFFFFFF ;s1的地址为0x0012FF78,其数据内容如下:
0012FF78: 61 CC CC CC FF FF FF FF中间夹杂了3个字节的CC看看MSDN上的说明:When applied to a
structure type or variable, sizeof returns the actual size, which may
include padding bytes ed for alignment.
这就是字节对齐!为什么需要字节对齐计算机组成原理教导我们这样有助于加快计算机的取数速度,否则
就得多花指令周期了。为此,编译器默认会对结构体进行处理(实际上其它地方的数
据变量也是如此),让宽度为2的基本数据类型(short等)都位于能被2整除的`地址上,让宽度为4的基本
数据类型(int等)都位于能被4整除的地址上,以此类推。这样,两个数中间就可能
需要加入填充字节,所以整个结构体的sizeof值就增长了。
1、sizeof是运算符,跟加减乘除的性质其实是一样的,在编译的时候进行执行,而不是在运行时才执行。
那么如果编程中验证这一点呢?
复制代码 代码如下:
#include
using namespace std;
int main()
int i=1;
cout<
sizeof(++i);
cout<
return 1;
输入结果为 1
1
sizeof 中的++i 的副作用并没有显示出来,原因只可能有一个,在编译的时候sizeof执行以后将++i 处理了,++i 的副作用因此被消除了。如果sizeof 是在运行时进行的话,则肯定要注意++i 。实际上sizeof的实现应该是用宏来做的,宏在编译时进行执行。具体实现可以参考下面。
2、sizeof('a')在C语言中的结果是4,在C++中结果是1,看过某篇文章说C中sizeof侧重于“数”,而C++中sizeof更侧重于“字符”。
3、文章中讲了两个用宏实现sizeof的经典应用
复制代码 代码如下:
//适用于非数组
#define _sizeof(T) ((size_t)((T*)0 + 1))
//适用于数组
#define array_sizeof(T) ((size_t)(&T+1)-(size_t)(&T))
先举两个小例子说明两个宏的应用,对于第一个如 _sizeof(int); 的结果就是4;对于第二个先声明一个大小为4的数组int a[4];那么array_sizeof(a)结果为16.
对于非数组的宏定义,先是将0转换为T*类型的指针所指向的地址(此时地址为0)。然后对T类型的地址加1,相当于加上了T类型的大小(即得到了非数组T的大小)。前面的size_t只是将地址转化为int型的整数返回。
一个简单的例子:int* p; p=p+1; ——p是一个int*类型的指针, p+1在地址空间上相当于加上了4个字节。
对 于数组的宏定义,类似于非数组的宏定义,为了方便理解,这里可以把数组T看成一个用户自定义的类型,&T表示数组类型的指针,对于数组类型指针加 1相当于在地址上加上了该数组大小。由于是用户自定义的类型所以不能强制将0转化为数组类型的地址,只能用加1后的地址减去之前的地址,得到的差值就是数 组本身所占的字节大小。
拓展阅读:C语言变量理解
一、变量的概念
程序设计就是让计算机按照一定的指令来进行工作,可以说数据的处理是程序设计的主要任务。那么数据是怎么加入到计算机的内存中呢?计算机最初的功能就是能够存储数据并处理数据的机器。那么数据是怎么加入到计算机的内存中呢?在学习程序设计之前,很多学生对计算机的硬件设施都大概已经很熟悉了。计算机的硬件设施中有一个区域是用来存储数据的,计算机在工作的过程中会频繁的从这个区域读入和读出数据。要想让计算机按照某些指令(程序)自动工作,首先必须把数据存储到计算机的存储空间中。在某种计算机语言中实现这种数据存储功能的就是变量。变量就是计算机内存中的某一个存储单元。
二、变量的定义
C语言中变量在使用之前必须先对其进行定义,变量的定义的一般形式如下:【存储类别】数据类型变量名;其中存储类别是可以省略的。
1、存储类别计算机的内存一般分为三个部分:1)程序区;2)静态存储区;3)动态存储区;为了高效的发挥计算机的功能,不同类型的变量存放在不同的内存区域。变量的存储类别决定了变量中的数据在计算机内存中的存储位置。C语言中局部变量存放在动态存储区,全局变量或者静态变量存放在静态存储区。
2、数据类型在用程序处理问题之前,首先必须确定用何种方式描述问题中所涉及到的数据。这个问题在C语言中是由数据类型来决定的。变量的本质表现为在计算机中的存在时间和存储空间。变量的数据类型是用来决定变量在计算机中占用内存空间的大小。比如:整型数据在TC编译系统中占用两个字节的存储空间。C语言变量的类型不仅确定了数据在计算机内存中的存储区域的大小,同时确定了该数据能够参与的各种运算。任何一个C语言的变量必须有确定的数据类型,不管这个变量如何变化,变量的值都必须符合该变量数据类型的规定。
3、变量的名字通过以上介绍我们知道C语言中的变量就是计算机的某个存储单元,假设你给某个变量赋予了一个数值,对变量的处理其实就是对这个数据的处理。那么计算机是如何快速准确的找到这个数据呢?计算机的内存是以字节为单位进行划分的。每个存储单元都有自己的地址编号,就向宾馆中房间的房间号一样。计算机就是通过地址来准确的确定数据的存储位置。但是对于程序员特别是非专业计算机人士,如果用计算机内存地址记录数据是非常难操作的。
为了更好的掌控变量,C语言规定可以给每个变量其一个容易识别的名字。这个名字的命名规则遵循C语言的标识符命名规则。C语言的变量名的命名在遵循C语言标识符规则的前提下,原则上可以是任意长度字符的组合。但是目前很多的C语言编译系统只能识别前31个字符,如果两个变量的前31个字符相同,则编译系统会认为这两个变量时同一个变量。为了避免这种混淆的出现最好避免使用多余31个字符的变量名。变量的理解变量就是计算机中的某个存储单元。定义某个变量本质上就是向计算机申请一些存储区域。这个存储区域的大小由变量的数据类型决定,这个存储区域的位置有变量的存储类类别决定。给变量赋予某个数值,其实就是向该变量对应的存储单元读入数据,对变量的处理就是对这个存储单元中的数据的处理。并且这个存储单元中的数据在程序的运行期间是可以发生变化的。
C中的基本数组用法?
【中文标题】C中的基本数组用法?【英文标题】:Basic array usage in C? 【发布时间】:2011-08-08 08:56:02 【问题描述】:这就是你们在 ANSI-C99 中获取数组大小的方法吗?看起来有点笨拙,来自高等语言。
int tests[7];
for (int i=0; i<sizeof(tests)/sizeof(int); i++)
tests[i] = rand();
这也是分段错误。
int r = 10000000;
printf ("r: %i\n", r);
int tests[r];
运行它:
r: 10000000
Segmentation fault
10000000 段错误,但 1000000 有效。
如何从中获取更多信息?我应该检查什么以及如何调试这样的东西? C 数组有限制吗?什么是分段错误?
【问题讨论】:
所以!尝试在堆上而不是在堆栈上分配。 【参考方案1】:在 C 中获取数组的大小很容易。这将为您提供数组的大小(以字节为单位)。
sizeof(x)
但我猜你需要的是元素的数量,在这种情况下它将是:
sizeof(x) / sizeof(x[0])
您可以为此编写一个简单的宏:
#define NumElements(x) (sizeof(x) / sizeof(x[0]))
例如:
int a[10];
int size_a = sizeof(a); /* size in bytes */
int numElm = NumElements(a); /* number of elements, here 10 */
【讨论】:
这里“byte”应该读作“char”,不一定是8位,可以更大。 @unwind,AFAIK,sizeof 为我们提供了以字节为单位的大小。如果不是这种情况,您能否详细说明一下? @unwind:C99 标准说返回值是以字节为单位的大小。 (第 6.5.3.4 节)。但是,它也说了各种使char
和“字节”或多或少相同的东西。
@JeremyP - 这不一定提供信息。 Byte 的本义(大约 100 年前,当它第一次被创造时)基本上是单个字符所需的空间。然而,单个字符所需的空间(可能在制表符的穿孔卡片上)当时可能不是 8 位。现代(至少 30 年)通用标准 8 位字节定义并未被普遍接受或“官方”,因此除非 C 标准明确说明一个字节(或一个字符)中有多少位。 .
@Steve314: byte 的本义无关。在 C99 中,明确表示 sizeof 以字节为单位返回大小。它还明确表示 sizeof(char) 为 1。它还明确表示在标准范围内,字节和字符是同一事物(还定义了宽字符和多字节字符等术语以解释 UTF-16 和UTF-8)。 C99 没有说一个字节有 8 位。【参考方案2】:
为什么要计算大小?
定义一个包含大小的常量,并在声明数组时使用它。在需要数组大小时引用该常量。
作为一个主要是 C++ 的程序员,我想说从历史上看,常量经常被定义为枚举值或 #define。不过,在 C 中,这可能是当前的而不是历史的 - 我不知道当前的 C 如何处理“const”。
如果你真的想计算大小,定义一个宏来做。甚至可能有一个标准的。
段错误的原因很可能是因为您尝试声明的数组价值约为 40 兆字节,并且被声明为局部变量。大多数操作系统都会限制堆栈的大小。将您的数组保存在堆或全局内存中,一个变量 40 兆字节对于大多数系统来说可能是可以的,尽管一些嵌入式系统可能仍然很糟糕。在像 Java 这样的语言中,所有对象都在堆上,只有引用保存在栈上。这是一个简单而灵活的系统,但通常比在堆栈上存储数据效率低得多(堆分配开销、可避免的堆碎片、间接访问开销......)。
【讨论】:
谢谢。我如何告诉我的堆栈限制以及如何调试它以确定是否是这种情况? @user697111 - 老实说我不知道。我知道这个大小限制是存在的,比许多人想象的要小,并且因平台而异,但我从来没有比这更担心。多年来我一直不担心多 KB 的本地变量,但即使是那些也不是典型的——大对象往往都存在于堆中,因为它们往往寿命长,并且比创建它们的函数寿命长。此外,即使我知道我的机器的确切数字,我也无法使用这些知识 - 其他人的机器会有更小的限制。 这可能有帮助:***.com/questions/53827/… @KDM - 很好的链接 - 我特别喜欢 Steve Jessops 对已接受答案的“沉默的阴谋”评论。 值得补充的一点 - 这个堆栈大小问题是首选迭代算法而不是递归的原因,除非您可以确信您的编译器会将递归转换为迭代。这些保证在函数式语言中很常见,但在 C 中没有保证。在实践中,像 GCC 这样的编译器可以在更多情况下优化迭代,而不仅仅是尾递归,但是(特别是在 C++ 中而不是 C 中)思考某些东西的可能性当它不是问题时会优化。 “优化”通常不是优化 - 这是一个正确性问题。【参考方案3】:C 中的数组不知道它们有多大,所以是的,您必须使用 sizeof array / sizeof array[0]
技巧来获取数组中的元素数量。
至于段错误问题,我猜您尝试分配 10000000 * sizeof int
字节超出了堆栈大小。经验法则是,如果您需要超过几百个字节,请使用malloc
或calloc
动态分配它,而不是尝试创建一个大的auto
变量:
int r = 10000000;
int *tests = malloc(sizeof *test * r);
请注意,在大多数情况下,您可以将tests
视为它是一个数组类型(即,您可以为它下标,您可以将它传递给任何需要数组的函数,等等。 ),但它不是数组类型;它是一个指针类型,所以 sizeof tests / sizeof tests[0]
技巧不起作用。
【讨论】:
【参考方案4】:传统上,数组具有静态大小。所以我们可以做
#define LEN 10
int arr[LEN];
但不是
int len;
scanf("%d", &len);
int arr[len]; // bad!
由于我们在编译时就知道数组的大小,因此获取数组的大小往往是微不足道的。我们不需要sizeof
,因为我们可以通过查看我们的声明来确定大小。
C++ 提供堆数组,如
int len;
scanf("%d", &len);
int *arr = new int[len];
但是由于这涉及指针而不是堆栈数组,我们必须将大小存储在我们手动传递的变量中。
【讨论】:
严格来说,这些“堆数组”与 C 数组没有太大区别——尤其是对于 int 或 C++ 认为“普通旧数据”的其他类型。它是 malloc 的“语法糖”,除了构造函数处理,它与 POD 无关。实际上,严格来说,C++ 可以为 new 使用单独的堆——你不应该释放你的新内容,或者删除你 malloc 的内容,无论构造函数/析构函数问题如何——但它仍然只是分配一个至少正确大小的内存块.【参考方案5】:我怀疑是因为整数溢出。尝试使用 printf 打印值:
printf("%d", 10000000);
如果它打印一个负数 - 这就是问题所在。
【讨论】:
printf ("%i\n", 10000000); printf ("%i\n", 100000000); printf ("大小: %i\n", sizeof(int)); 10000000 100000000 大小:4 32 位整数不会为此溢出。 24 位有符号整数将失败(仅 - 1000 万作为无符号 24 位可以),但使用 24 位地址(单字节值数组除外)转换为存储位置的数学运算将失败。具有 24 位类型的架构很少见,但过去至少有一些。【参考方案6】:堆栈溢出!尝试在堆而不是堆栈上分配。
【讨论】:
以上是关于C语言中sizeof的用法的主要内容,如果未能解决你的问题,请参考以下文章