关于 sizeof.(网上搜到的一些文章)

在网上搜到的关于sizeof 的资料。分享一下。

首先看一 MSDN 上如何对 sizeof 进行定义的:
sizeof Operator
 
sizeof expression
 
The sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type
(including aggregate types). This keyword returns a value of type size_t.
 
The expression is either an identifier or a type-cast expression (a type specifier enclosed in
parentheses).
 
When applied to a structure type or variable, sizeof returns the actual size, which may include
padding bytes inserted for alignment. When applied to a statically dimensioned array, sizeof
returns the size of the entire array. The sizeof operator cannot return the size of dynamically
allocated arrays or external arrays.
然后再看一下对 strlen 是如何定义的:
strlen
 
Get the length of a string.
 
Routine Required Header
strlen <string.h>
 
size_t strlen( const char *string );
Parameter
string :Null-terminated string
Libraries
All versions of the C run-time libraries.
 
Return Value
Each of these functions returns the number of characters in string, excluding the terminal
NULL. No return value is reserved to indicate an error.
 
Remarks
Each of these functions returns the number of characters in string, not including the
terminating null character. wcslen is a wide-character version of strlen; the argument of
wcslen is a wide-character string. wcslen and strlen behave identically otherwise.
二、由几个例子说开去。

第一个例子:
char* ss = "0123456789";
sizeof(ss) 结果 4 ===》ss是指向字符串常量的字符指针
sizeof(*ss) 结果 1 ===》*ss是第一个字符
 
char ss[] = "0123456789";
sizeof(ss) 结果 11 ===》ss是数组,计算到/0位置,因此是10+1
sizeof(*ss) 结果 1 ===》*ss是第一个字符
 
char ss[100] = "0123456789";
sizeof(ss) 结果是100 ===》ss表示在内存中的大小 100×1
strlen(ss) 结果是10 ===》strlen是个函数内部实现是用一个循环计算到/0为止之前
 
int ss[100] = "0123456789";
sizeof(ss) 结果 400 ===》ss表示再内存中的大小 100×4
strlen(ss) 错误 ===》strlen的参数只能是char* 且必须是以''/0''结尾的
 
char q[]="abc";
char p[]="a/n";
sizeof(q),sizeof(p),strlen(q),strlen(p);
结果是 4 3 3 2     
第二个例子:
class X
{
int i;
int j;
char k;
};
X x;
cout<<sizeof(X)<<endl; 结果 12 ===》内存补齐
cout<<sizeof(x)<<endl; 结果 12 同上
第三个例子:
char szPath[MAX_PATH]
  如果在函数内这样定义,那么 sizeof(szPath) 将会是 MAX_PATH ,但是将 szPath 作为虚参声明时( void fun(char szPath[MAX_PATH]) ,sizeof(szPath) 却会是 4( 指针大小 )

三、 sizeof 深入理解。
  • 1.sizeof操作符的结果类型是size_t,它在头文件中typedefunsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小。
  • 2.sizeof是算符,strlen是函数。
  • 3.sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以''/0''结尾的。sizeof还可以用函数做参数,比如:
·          short f();
·          printf("%d/n", sizeof(f()));
输出的结果是 sizeof(short) ,即 2
  • 4.数组做sizeof的参数不退化,传递给strlen就退化为指针了。
  • 5.大部分编译程序在编译的时候就把sizeof计算过了是类型或是变量的长度这就是sizeof(x)可以用来定义数组维数的原因
·          char str[20]="0123456789";
·          int a=strlen(str); //a=10;
·          int b=sizeof(str); // 而b=20;
  • 6.strlen的结果要在运行的时候才能计算出来,时用来计算字符串的长度,不是类型占内存的大小。
  • 7.sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof是个操作符不是个函数。
  • 8.当适用了于一个结构类型时或变量, sizeof 返回实际的大小,当适用一静态地空间数组, sizeof 归还全部数组的尺寸。 sizeof 操作符不能返回动态地被分派了的数组或外部的数组的尺寸
  • 9.数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,如:
·          fun(char [8])
·          fun(char [])
都等价于 fun(char *) C++ 里传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小如果想在函数内知道数组的大小, 需要这样做:进入函数后用 memcpy 拷贝出来,长度由另一个形参传进去
fun(unsiged char *p1, int len)
{
 unsigned char* buf = new unsigned char[len+1]
 memcpy(buf, p1, len);
}
有关内容见: C++ PRIMER?
  • 10.计算结构变量的大小就必须讨论数据对齐问题。为了CPU存取的速度最快(这同CPU取数操作有关,详细的介绍可以参考一些计算机原理方面的书),C++在处理数据时经常把结构变量中的成员的大小按照48的倍数计算,这就叫数据对齐(data alignment)。这样做可能会浪费一些内存,但理论上速度快了。当然这样的设置会在读写一些别的应用程序生成的数据文件或交换数据时带来不便。MS VC++中的对齐设定,有时候sizeof得到的与实际不等。一般在VC++中加上#pragma pack(n)的设定即可.或者如果要按字节存储,而不进行数据对齐,可以在Options对话框中修改Advanced compiler页中的Data alignment为按字节对齐。
  • 11.sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。如sizeof(max)若此时变量max定义为int max(),sizeof(char_v) 若此时char_v定义为char char_v [MAX]MAX未知,sizeof(void)都不是正确形式
四、结束语

sizeof
使用场合。
  • 1.sizeof操作符的一个主要用途是与存储分配和I/O系统那样的例程进行通信。例如: 
·            void *malloc(size_t size), 
·            size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream)。
  • 2.用它可以看看一类型的对象在内存中所占的单元字节。
·          void  * memset(void * s,int c,sizeof(s))
  • 3.在动态分配一对象时,可以让系统知道要分配多少内存。
  • 4.便于一些类型的扩充,windows中就有很多结构内型就有一个专用的字段是用来放该类型的字节大小。
  • 5.由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用sizeof来代替常量计算。
  • 6.如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。
解析 C 语言中的 sizeof
一、 sizeof 的概念 
   sizeof C 语言的一种单目操作符,如 C 语言的其他操作符 ++ -- 等。它并不是函数。 sizeof 操作符以字节形式给出了其操作数的存储大小。操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的类型决定。 
二、 sizeof 的使用方法 
   1 、用于数据类型 
   sizeof 使用形式: sizeof type ) 
  数据类型必须用括号括住。如 sizeof int )。 
   2 、用于变量 
   sizeof 使用形式: sizeof var_name )或 sizeof   var_name  
  变量名可以不用括号括住。如 sizeof   (var_name) sizeof   var_name 等都是正确形式。带括号的用法更普遍,大多数程序员采用这种形式。 
  注意: sizeof 操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、 void 类型等。 
  如 sizeof(max) 若此时变量 max 定义为 int   max(),sizeof(char_v)  若此时 char_v 定义为 char   char_v   [MAX] MAX 未知, sizeof(void) 都不是正确形式。 
三、 sizeof 的结果 
   sizeof 操作符的结果类型是 size_t ,它在头文件
typedef unsigned   int 类型。该类型保证能容纳实现所建立的最大对象的字节大小。 
   1 、若操作数具有类型 char unsigned   char signed   char ,其结果等于 1 。 
   ANSI   C 正式规定字符类型为 1 字节。 
   2 int unsigned   int  、 short   int unsigned   short  、 long   int  、 unsigned   long  、 float double long   double 类型的 sizeof  在 ANSI   C 中没有具体规定,大小依赖于实现,一般可能分别为 2 2 2 2 4 4 4 8 10 。 
   3 、当操作数是指针时, sizeof 依赖于编译器。例如 Microsoft   C/C++7.0 中, near 类指针字节数为 2 far huge 类指针字节数为 4 。一般 Unix 的指针字节数为 4 。 
   4 、当操作数具有数组类型时,其结果是数组的总字节数。 
   5 、联合类型操作数的 sizeof 是其最大字节成员的字节数。结构类型操作数的 sizeof 是这种类型对象的总字节数,包括任何垫补在内。 
  让我们看如下结构: 
   struct   {char   b;   double   x;}   a;  
  在某些机器上 sizeof a =12 ,而一般 sizeof char +   sizeof double =9 。 
  这是因为编译器在考虑对齐问题时,在结构中插入空位以控制各成员对象的地址对齐。如 double 类型的结构成员 x 要放在被 4 整除的地址。 
   6 、如果操作数是函数中的数组形参或函数类型的形参, sizeof 给出其指针的大小。 
四、 sizeof 与其他操作符的关系 
   sizeof 的优先级为 2 级,比 / % 3 级运算符优先级高。它可以与其他操作符一起组成表达式。如 i*sizeof int );其中 i int 类型变量。 
五、 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 来代替常量计算。

本文主要包括二个部分,第一部分重点介绍在 VC 中,怎么样采用 sizeof 来求结构的大小,以及容易出现的问题,并给出解决问题的方法,第二部分总结出 VC sizeof 的主要用法。
1 sizeof 应用在结构上的情况
请看下面的结构:
struct MyStruct
{
double dda1;
char dda;
int type
};
对结构 MyStruct 采用 sizeof 会出现什么结果呢? sizeof(MyStruct) 为多少呢?也许你会这样求:
sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13
但是当在 VC 中测试上面结构的大小时,你会发现 sizeof(MyStruct) 16 。你知道为什么在 VC 中会得出这样一个结果吗?
其实,这是 VC 对变量存储的一个特殊处理。为了提高 CPU 的存储速度, VC 对一些变量的起始地址做了 对齐 处理。在默认情况下, VC 规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式 (vc6.0,32 位系统 )
类型
对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char
偏移量必须为 sizeof(char) 1 的倍数
int
偏移量必须为 sizeof(int) 4 的倍数
float
偏移量必须为 sizeof(float) 4 的倍数
double
偏移量必须为 sizeof(double) 8 的倍数
Short
偏移量必须为 sizeof(short) 2 的倍数
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节 VC 会自动填充。同时 VC 为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
下面用前面的例子来说明 VC 到底怎么样来存放结构的。
struct MyStruct
{
double dda1;
char dda;
int type
}
为上面的结构分配空间的时候, VC 根据成员变量出现的顺序和对齐方式,先为第一个成员 dda1 分配空间,其起始地址跟结构的起始地址相同(刚好偏移量 0 刚好为 sizeof(double) 的倍数),该成员变量占用 sizeof(double)=8 个字节;接下来为第二个成员 dda 分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为 8 ,是 sizeof(char) 的倍数,所以把 dda 存放在偏移量为 8 的地方满足对齐方式,该成员变量占用 sizeof(char)=1 个字节;接下来为第三个成员 type 分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为 9 ,不是 sizeof(int)=4 的倍数,为了满足对齐方式对偏移量的约束问题, VC 自动填充 3 个字节(这三个字节没有放什么东西),这时下一个可以分配的地址对于结构的起始地址的偏移量为 12 ,刚好是 sizeof(int)=4 的倍数,所以把 type 存放在偏移量为 12 的地方,该成员变量占用 sizeof(int)=4 个字节;这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为: 8+1+3+4=16 ,刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数 sizeof(double)=8 )的倍数,所以没有空缺的字节需要填充。所以整个结构的大小为: sizeof(MyStruct)=8+1+3+4=16 ,其中有 3 个字节是 VC 自动填充的,没有放任何有意义的东西。
下面再举个例子,交换一下上面的 MyStruct 的成员变量的位置,使它变成下面的情况:
struct MyStruct
{
char dda;
double dda1;
int type
}
这个结构占用的空间为多大呢?在 VC6.0 环境下,可以得到 sizeof(MyStruc) 24 。结合上面提到的分配空间的一些原则,分析下 VC 怎么样为上面的结构分配空间的。(简单说明)
struct MyStruct
{
char dda;// 偏移量为 0 ,满足对齐方式, dda 占用 1 个字节;
double dda1;// 下一个可用的地址的偏移量为 1 ,不是 sizeof(double)=8
// 的倍数,需要补足 7 个字节才能使偏移量变为 8 (满足对齐
// 方式),因此 VC 自动填充 7 个字节, dda1 存放在偏移量为 8
// 的地址上,它占用 8 个字节。
int type // 下一个可用的地址的偏移量为 16 ,是 sizeof(int)=4 的倍
// 数,满足 int 的对齐方式,所以不需要 VC 自动填充, type
// 放在偏移量为 16 的地址上,它占用 4 个字节。
} // 所有成员变量都分配了空间,空间总的大小为 1+7+8+4=20 ,不是结构
// 的节边界数(即结构中占用最大空间的类型所占用的字节数 sizeof
//(double)=8 )的倍数,所以需要填充 4 个字节,以满足结构的大小为
//sizeof(double)=8 的倍数。
所以该结构总的大小为: sizeof(MyStruc) 1+7+8+4+4=24 。其中总的有 7+4=11 个字节是 VC 自动填充的,没有放任何有意义的东西。
VC 对结构的存储的特殊处理确实提高 CPU 存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。
VC 中提供了 #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 ,下面分析其存储情况,首先为 m1 分配空间,其偏移量为 0 ,满足我们自己设定的对齐方式( 4 字节对齐), m1 占用 1 个字节。接着开始为 m4 分配空间,这时其偏移量为 1 ,需要补足 3 个字节,这样使偏移量满足为 n=4 的倍数(因为 sizeof(double) 大于 n ,m4 占用 8 个字节。接着为 m3 分配空间,这时其偏移量为 12 ,满足为 4 的倍数, m3 占用 4 个字节。这时已经为所有成员变量分配了空间,共分配了 16 个字节,满足为 n 的倍数。如果把上面的 #pragma pack(4) 改为 #pragma pack(16) ,那么我们可以得到结构的大小为 24 。(请读者自己分析)
2 sizeof 用法总结
VC 中, sizeof 有着许多的用法,而且很容易引起一些错误。下面根据 sizeof 后面的参数对 sizeof 的用法做个总结。
A 参数为数据类型或者为一般变量。例如 sizeof(int),sizeof(long) 等等。这种情况要注意的是不同系统系统或者不同编译器得到的结果可能是不同的。例如 int 类型在 16 位系统中占 2 个字节,在 32 位系统中占 4 个字节。
B 参数为数组或指针。下面举例说明 .
int a[50]; //sizeof(a)=4*50=200; 求数组所占的空间大小
int *a=new int[50];// sizeof(a)=4; a 为一个指针, sizeof(a) 是求指针
// 的大小 , 32 位系统中,当然是占 4 个字节。
C 参数为结构或类。 Sizeof 应用在类和结构的处理情况是相同的。但有两点需要注意,第一、结构或者类中的静态成员不对结构或者类的大小产生影响,因为静态变量的存储位置与结构或者类的实例地址无关。
第二、没有成员变量的结构或类的大小为 1 ,因为必须保证结构或类的每一
个实例在内存中都有唯一的地址。
下面举例说明,
Class Test{int a;static double c};//sizeof(Test)=4.
Test *s;//sizeof(s)=4,s 为一个指针。
Class test1{ };//sizeof(test1)=1;
D 参数为其他。下面举例说明。
int func(char s[5]);
{
cout<
//
数的参数在传递的时候系统处理为一个指针,所
// sizeof(s) 实际上为求指针的大小。
return 1;
}
sizeof(func(“1234”))=4// 因为 func 的返回类型为 int ,所以相当于
// sizeof(int).
以上为 sizeof 的基本用法,在实际的使用中要注意分析 VC 的分配变量的分配策略,这样的话可以避免一些错误。

9
struct sizeof 问题
    因为对齐问题使结构体的 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 判断结构体的对界,根据上一节的结论, s1 s2 的对界都取最大的元素类型,也就是 double 类型的对界 8 。然后开始摆放每个元素。
   
对于 s1 ,首先把 a 放到 8 的对界,假定是 0 ,此时下一个空闲的地址是 1 ,但是下一个元素 d double 类型,要放到 8 的对界上,离 1 最接近的地址是 8 了,所以 d 被放在了 8 ,此时下一个空闲地址变成了 16 ,下一个元素 c 的对界是 4 16 可以满足,所以 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 的倍数。
    这里有个陷阱,对于结构体中的结构体成员,不要认为它的对齐方式就是他的大小,看下面的例子:
struct s1
{
  char a[8];
};
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;
    s1 s2 大小虽然都是 8 ,但是 s1 的对齐方式是 1 s2 8 double ),所以在 s3 s4 中才有这样的差异。
    所以,在自己定义结构体的时候,如果空间紧张的话,最好考虑对齐因素来排列结构体里的元素。
10 、不要让 double 干扰你的位域
    在结构体和类中,可以使用位域来规定某个成员所能占用的空间,所以使用位域能在一定程度上节省结构体占用的空间。不过考虑下面的代码:
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 类型放在程序的开始或者最后。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值