首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

LINUX 下嵌入式笔试的重要的知识点(sizeof) zz

LINUX 下嵌入式笔试的重要的知识点(sizeof) zz

LINUX 下嵌入式笔试的重要的知识点(sizeof)  zz


最近去了几家公司面试 LINUX 下嵌入式软件工程师,发现几个公司的笔试题目中有几个知识点是几乎是必考的知识点。在这里总结一下。
    (1):SIZEOF
          1、定义:sizeof是C/C++中的一个操作符(operator)是也,简单的说其作用就是返回一个对象或者类型所占的内存字节数。
          2、语法格式 sizeof有三种语法形式,如下:
             1) 用于数据类型  sizeof( type_name ); // sizeof( 类型 );
             2) 用于变量  sizeof( object ); // sizeof( 对象 );
                          sizeof object; // sizeof 对象;
            所以,
            int i;
            sizeof( i ); // ok
            sizeof i; // ok
            sizeof( int ); // ok
            sizeof int; // error
            注意:sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。 
  如sizeof(max)若此时变量max定义为int max(),sizeof(char_v) 若此时char_v定义为char char_v [MAX]且MAX未知,sizeof(void)都不是正确形式。
          3、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给出其指针的大小。
        4、sizeof与其他操作符的关系 
  sizeof的优先级为2级,比/、%等3级运算符优先级高。它可以与其他操作符一起组成表达式。如i*sizeof(int);其中i为int类型变量。 
        5、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))。 
        6、建议 
  由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用sizeof来代替常量计算。  
       (2)sizeof用法
          假设以后的程序都是32位C的程序。
          int i=10;
          在这里由于是32位的C 程序,故对于一个整型变量应该用32位来存放,
          sizeof(i)为4字节;
          sizeof(int)也为4字节;
         
          char a='b';
          char b[]="hello,world";
          char c[20]="hello,world";
          char *p=b;
          明显sizeof(a)为1字节;
          b为数组名sizeof是求取数组所占内存的大小,故sizeof(b)为12,可能大家会注意到hello,world仅有11个字符,然而为什么要站12字节呢? 这是因为在没有指定数组大小的时,为数组初始化后会在后面加上结束符;因此为12。
          因为 p指针变量是一个地址,地址信息又用32位来存放,所以sizeof(p)为4;
          sizeof(*p)为数组b的大小。;
          由于在定义数组时指定了数组的大小sizeof(c)故为20,在这里hello,world仅为11字符,在后面回自动用0填充满整个数组;
         
         我们对sizeof有初步的了解了,再接下来看这样的一个结构
         struct 1{  
            int a;
           char b;
            int c;
         } ;
        问sizeof(s1)等于多少?或许你是这样想的,char占1个字节,int占4个字节,那么加起来就应该是9。很可能你是错的!VC6中按默认设置得到的结果为12,在32位C中也为12。那仔细分析一下为什么为12吧!
        如果是用那样来进行存储的的话,那么在读取这些变量时PC指针一会加4,而一会加1,一会又加4 ,PC加减没有一固定值,很明显影响系统的效率。在编译原理为了提高系统的效率采用了内存对齐技术。在后面详细分析内存对齐。
        在这里主要看一下char在这里占的空间。这里是32为的程序,故int占4个字节,没有调整内存对齐的方式,故应该按照默认的方式进行内存对齐。在此结构里面应按照4个字节对齐效率最好。故char在这里占的空间也应该为4个字节。后面有3填充字节。
        在分析下面的结构
        struct s2{  
            int a;
           char b;
            int c;
            s1  s;
         } ;
         sizeof(s2)等于多少? 为24字节。
         #i nclude<stdio.h>
         void print(char a[])
         { printf("%d\n",sizeof(a));
         }
         main()
         { char a[]="hello,world";
           print(a);
         }
         运行的结果是什么呢? 或许你会认为结果是12。在print(a)的实参仅仅将数组的地址传给形参,而不是将整个数组作为实参传递。所以既然形参接受的是一个地址,那结果应是4。
         
         字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
            1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
            2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
            3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
            对于上面的准则,有几点需要说明:
           1) 前面不是说结构体成员的地址是其大小的整数倍,怎么又说到偏移量了呢因为有了第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的大小。由于结构体的成员可以是复合类型,比如另外一个结构体,所以在寻找最宽基本类型成员时,应当包括复合类型成员的子成员,而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。
          含位域结构体的sizeof
前面已经说过,位域成员不能单独被取sizeof值,我们这里要讨论的是含有位域的结构体的sizeof,只是考虑到其特殊性而将其专门列了出来。
C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。使用位域的主要目的是压缩存储,其大致规则为:
          1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
          2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
          3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
          4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
          5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
还是让我们来看看例子。
示例:
struct BF1
{
char f1 : 3;
char f2 : 4;
char f3 : 5;
};
其内存布局为:
|_f1__|__f2__|_|____f3___|____|
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
0 3 7 8 1316
位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只
能从下一个字节开始。因此sizeof(BF1)的结果为2。
示例:
struct BF2
{
char f1 : 3;
short f2 : 4;
char f3 : 5;
};
由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。
示例:
struct BF3
{
char f1 : 3;
char f2;
char f3 : 5;
};
非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。
        联合体的sizeof
结构体在内存组织上是顺序式的,联合体则是重叠式,各成员共享一段内存,所以整个联合体的sizeof也就是每个成员sizeof的最大值。结构体的成员也可以是复合类型,这里,复合类型成员是被作为整体考虑的。
所以,下面例子中,U的sizeof值等于sizeof(s)。
union U
{
int i;
char c;
S1 s;
};
              ---------补充----------------------------
参数为结构或类。Sizeof应用在类和结构的处理情况是相同的。但有三点需要注意:
第一、结构或者类中的静态成员不对结构或者类的大小产生影响,因为静态变量的存储位置与结构或者类的实例地址无关。
第二、没有成员变量的结构或类的大小为1,因为必须保证结构或类的每一个实例在内存中都有唯一的地址。
第三、类中含有虚函数的情况,虚函数表指针占用4个字节大小,并且放在类的开头。即虚函数在类中的位置对类的大小无影响,其始终是在最前面的。
下面举例说明,
1、Class Test{int a;static double c};//sizeof(Test)=4.
2、Test *s;//sizeof(s)=4,s为一个指针。
3、Class test1{ };//sizeof(test1)=1;
4、class   A
{
   double  a;
   char  b;
   virtual   p(){};
};//sizeof(A)=24,因为类中有虚函数,所以等价于下面:
class A
{
    void *p; //虚函数表指针
    double a;
    char  b;
};

返回列表