结构体与联合体面试题

结构体

对齐的作用和原因:各个硬件平台对存储空间的处理上有 很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种 架构下编程必须保证字节对齐,其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些 平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇 地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据,显然在读取效率上下降很多。

结构体对齐原则:

结构体中元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,每一个元素放置到内存中时,它都会认为内存是以它自己的大小来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始(以结构体变量首地址为0计算),结构体的存储单元是否为结构体元素中最宽的元素的长度的整数倍是,则结束;若不是,则补齐为它的整数倍

如果存在一个结构体:

struct A{  //struct是关键字, A是结构体标志   
 char a;
 int b; //a,b是结构体成员列表
   
}s1;       //s1是结构体变量
0char  a
1填充
2填充
3填充
4int  b
5
6
7

2 :

 struct X
{
    char a;
    double b;
    int c;
}S2;
0char a
8double b
 
12
15
16int c
23

================================================================================================

很有意思的一个题

    struct X1 {char a; char b; char c; char d; char e;}A;   
    struct X2 {short a; short b; short c;}B;    
    struct X3 {short a; long b; char c;}C;

       sizeof(A) =5, sizeof(B) = 6, sizeof(C) = 12  /  //64位机器

       结构体A中的元素都是char类型,每一个元素都是紧挨着排列的,所以5个char 占5个字节,再看结构体中最大类型还是                char,结构体最终的大小必须是结构体里面最大类型元素的整数倍,5个字节 是char所占内存的整数倍。

      结构体C中 short a 占2个字节,long占4字节, short a放在结构体的首地址占两个

short a填充填充bc填充填充填充

                      0                            2           3           4        8      9                                     12

    

   struct X  { char a; double b; int c; }S2;   
   struct X3 {short a; long b; char c; X S2;}C;
   struct X4 {short a; long b; char c; char d; double e; int f;}D;

      sizeof(C)= 40      sizeof(D)= 32    //64 位机

     结构体C   0表示填充

a00bc0000000s2.a0000000S2.bs2.c0000

                     2+2+4+1+7+(1+7+8+4+4)  // 括号里面的是S2结构体的大小

   结构体D 

a00bcs2.a0000000S2.bs2.c0000

                    2+2+4+1+1+6+8+4+4 =28   结构体大小必须是结构体里(不管嵌套几个结构体)最大类型的整数倍  -》 32

================================================================================================

以上都是使用的默认的对齐系数,我们可以指定对齐系数   

对齐数 = 对齐系数 与 该成员大小的较小值,

#pragma pack(n);中的n就是对齐系数。

VS中默认的值为8;linux中的默认值为4

#pragma pack(2)

struct X { char a; double b; int c; }S2; //  sizeof(S2)= 14

0char
1
2double
3
4
5
6
7
8
9
10int
11
12
13

#pragma pack(n) 影响的是第二个以及后面元素存放位置的起始地址,不使用该参数,第二个以及后面的元素偏移到自己类型大小的最小倍数处开始存放,如果使用了该参数,那么第二个以及后面的元素存放的位置起始地址需要满足%n==0 

结构体与位域

  如果结构体中含有位域(bit-field),那么VC中准则是:

  1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;

  2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

  3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;

  系统会先为结构体成员按照对齐方式分配空间和填塞(padding),然后对变量进行位域操作。

#include<stdio.h>

struct A
{
  char a;   // a 占用1字节 后面需要填充3字节
  int  b;   //  b  4个字节
  char  index; // index可以紧接着b排列,占用 1个字节  填充3个字节
};

struct test
{
  char a:7;  //占用7个比特位
  int  b:11; // b,c,d 加起来25个比特位  
  int  c:4;
  int  d:10; //a b c d  共占用32个比特位
  char  index; //占用 1个字节  填充3个字节
};

struct test2
{
   char a : 7;
   int b : 11;
   int c : 4;
   int d : 10;    //a b c d  共占用32个比特位 后面填充4个字节
   double index;  //占用 8个字节
  
};

struct test3
{
   char a : 7;
   int b : 11;
   int c : 4;
   int d : 10;  //a b c d  共占用32个比特位

};

struct test4
{
   char a : 7;
   int b : 11;
   int c : 14;  // a b c 共占32个比特位
   int d : 15;  //站4个字节

};

struct test5
{
   char a : 7;
   int b : 11;  //因为c不是位域,a b共同占用4个字节(有填充)
   float c;     //4字节
   int d : 15;  //4字节
};

struct test6
{
   char a[10]; //地址 0x7ffd284a3420
   int b : 11; //2字节 
   float c;    //地址 0x7ffd284a342c
};


int main(int argc, char *argv[])
{

  struct test  t1;
  struct test2 t2;
  struct test3 t3;
  struct test3 t4;
  struct test5 t5;
  struct test6 t6;
  
   struct A a;

  printf("size:%d\n",sizeof(a));
  printf("size:%d\n",sizeof(t1));
  printf("size:%d\n",sizeof(t2));
  printf("size:%d\n",sizeof(t3));
  printf("size:%d\n",sizeof(t4));
  printf("size:%d\n",sizeof(t5));
  printf("size:%d\n",sizeof(t6));
}

联合体:

利用union可以用相同的存储空间存储不同型别的数据类型,从而节省内存空间。在C Programming Language 一书中对于联合体是这么描述的:
1)联合体是一个结构;
2)它的所有成员相对于基地址的偏移量都为0;
3)此结构空间要大到足够容纳最"宽"的成员;
4)其对齐方式要适合其中所有的成员;

    union    
    {        
       unsigned short int i;        
       struct        
          {            
            char first;      // 75 A            
            char second;     // 76 B        
          }half;    
    } number;  
 
    number.i = 0x4241;    
    cout << number.half.first <<endl;    
    cout << number.half.second <<endl;    
    number.half.first = 0x31;    
    number.half.second = 0x32;   
    cout << number.i << endl;

================================================================================================

number.i = 0x4241  ,我们知道这是一个联合体,共用一块内存,struct half 地址里保存的值是0x4241,first  和 second各占一个字符,那么他们的值是多少呢?  这里我们需要知道另一个知识点-------大小端

大小端

大端模式:  数据的高字节存在低地址  数据的低字节存在高地址

小端模式:  数据的高字节存在高地址  数据的低字节存在低地址

 我们常用的x86结构都是小端模式,而大部分DSP,ARM也是小端模式,不过有些ARM是可以选择大小端模式

低地址高地址
half.firsthalf.second
0x410x42

number.half.first = 0x31;
number.half.second = 0x32;      number.i = 0x3231 

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值