首页 > 试题广场 >

写出下列程序在X86上的运行结果 。

[单选题]
写出下列程序在X86上的运行结果 
struct mybitfields
{
    unsigned short a : 4;
    unsigned short b : 5;
    unsigned short c : 7;
} test

void main(void)
{
    int i;
    test.a = 2;
    test.b = 3;
    test.c = 0;

    i = *((short *)&test);
    printf("%d\n", i);
}
  • 30
  • 50
  • 60
  • 20
推荐
这个题的为难之处呢,就在于前面定义结构体里面用到的冒号,如果你能理解这个符号的含义,那么问题就很好解决了。这里的冒号相当于分配几位空间,也即在定义结构体的时候,分配的成员a 4位的空间, b 5位,c 7位,一共是16位,正好两个字节。下面画一个简单的示意:
变量名  位数
test    15 14 13 12 11 10 9 |8 7 6 5 4 |3 2 1 0
test.a                      |          |0 0 1 0
test.b                      |0 0 0 1 1 |
test.c   0  0  0  0  0  0 0 |          |
在执行i=*((short *)&test); 时,取从地址&test开始两个字节(short占两个字节)的内容转化为short型数据,即为0x0032,再转为int型为0x00000032,即50
编辑于 2015-02-02 11:56:59 回复(20)
我是这么理解的,结构体中a,b,c从低地址到高地址分别占4,5,7位,main中赋值以后a=2,b=3,c=0.
又输出为short型为test的低16位的数,所以,为2+16*3=50
发表于 2015-10-04 11:21:59 回复(2)
位域运算:
a : 4 --------a占4位
b : 5 --------b占5为
c : 7 --------c占7位
所以 abc的二进制表示为:
a  ------  0010
b  ------  00011
c  ------  0000000

由于是小端模式,低地址低位(这里比较特殊)。且结构体a-c地址由低到高 ,内存如下:
0100
11000
0000000
a
b
c
在小端模式转化为short值为 0000 0000 0011 0010 = 50

Note:
小弟弟:小端模式低地址低字节;
内存按字节编址,一般来说,位的内存分布没有大小端概念,但是这道题是个列外,因为有位域运算;



编辑于 2020-01-19 10:42:31 回复(1)
前面说过的这里就不说了,就大家问的为什么是 c b a这样的顺序,做一个简单的说明。
栈确实是从高地址到低地址这样的方式增长,但是这结构体中,成员变量在结构体分得内存后,是从低地址到高地址分配的。
class my
{
	int a;
	int b;
	int c;
};
int main()
{
	my test;
	int x;
	int y;
}
调试结果为:
&test          0x0030f8b4 
&(test.a)     0x0030f8b4 
&(test.b)     0x0030f8b8 
&(test.c)     0x0030f8bc 
&x             0x0030f8a8 
&y             0x0030f89c 
从上面的数据可以看出,test,x,y确实从从高地址到低地址的,但是在test对象对应的结构体内部,a、b、c的地址是从小到大的。
所以对于本题,a在低地址处,c在高地址处,在小端方式中,高地址位对于高字节,所以最后组合的时候c放在高字节为,即c b a

编辑于 2017-08-04 20:11:45 回复(2)
内存分布 00000000 00110010   对于这个问题,首先小端模式,高位在前,单独写出c,b,a,的位,然后组装起来,从高到低。单独c,b,a的位是这样的 c 0000000 b 00011 a 0010。这些位的分布是因为无符号数,最高位所以为0,例如a为四位,最高位表示符号位0,其余三位就是010。另外还有位操作的内存对齐问题。内存大小为2个byte。为什么呢?请查看位操作的内存对齐资料。并且这里都是压缩存储,刚好满足16位。
发表于 2015-09-08 16:30:24 回复(5)
三个数共占16位,从低位到高位依次写出三个数的二进制形式,改成十进制就是最后的结果
发表于 2017-10-31 16:53:34 回复(2)
这个题的为难之处呢,就在于前面定义结构体里面用到的冒号,如果你能理解这个符号的含义,那么问题就很好解决了。这里的冒号相当于分配几位空间,也即在定义结构体的时候,分配的成员a 4位的空间, b 5位,c 7位,一共是16位,正好两个字节。下面画一个简单的示意:
变量名  位数
test    15 14 13 12 11 10 9 |8 7 6 5 4 |3 2 1 0
test.a                      |          |0 0 1 0
test.b                      |0 0 0 1 1 |
test.c   0  0  0  0  0  0 0 |          |
在执行i=*((short *)&test); 时,取从地址&test开始两个字节(short占两个字节)的内容转化为short型数据,即为0x0032,再转为int型为0x00000032,即50
发表于 2016-10-10 15:07:53 回复(0)
说实话,这道题我也不会,因为真没看懂,不理解 前面定义结构体里面用到的冒号
编辑于 2015-08-31 09:39:54 回复(2)
我来回答一下这个问题吧,结构体成员后加冒号表示为一个位段。
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。位段成员必须声明为int、unsigned int或signed int类型(short char long)
,例如:该题中unsigned short a :4表示a这个成员只占了4个bit位的空间,我们不必用上两个字节的空间也就是16个bit位,那么你想一想是不是节约了不少的空间呢,但是冒号后面的数肯定是不能超过16的,因为这是一个short型。我们把所有成员所占的空间相加得到4+5+7=16,刚好是一个short型的大小,我们用一个short型的空间就能全部存放下了。如果不用位段的话那么占用的空间为3个short型,那么这么一想是不是节省了不少空间。
其实位段跟结构体一样存在内存对齐,那么为什么会存在内存对齐呢?1.增加读取速度、更加方便。我们这样想假如没有内存对齐的话,我们可以不能一次读取出数据。假设不存在内存对齐,我们这里假设unsigned short a:14,a占了14个bit位,b占5个bit位,那么一个short只能存放16个bit位的空间,当放不下的时候我们的计算机每次就会另外开辟一个short型的空间,所以另外一块short型的空间中放了b的3个bit位的空间,我们的寄存器来读取时一次读取一个short型的空间,那么你想一下b是不是要两次才能读取出来,并且还要将两块空间连起来才能拼成一个b的值,这样就无形中带来了一定的麻烦;那么我们采用内存对齐就很好的解决了这个问题,当我们一个short存进a的14个bit位,只剩两个留给b了存不下b,所以我们就另外开辟一个short型的空间,这样寄存器一次就能将一个数完整的读取出来,加快了读取效率。另外关于还剩2个bit位的空间我们是被浪费掉了的还是用在其他地方我们并不清楚,但这并不影响还是在一定程度上节约了空间,具体看有没有节省空间看你给成员分配的空间有多大了,反正最坏的结果也是原来的空间相等。
接下来就正式进入正题,前面是先给大家梳理了一下知识点。
unsigned shorta : 4;
unsigned shortb : 5;
unsigned shortc : 7;
//给各成员赋值
test.a = 2;
test.b = 3;
test.c = 0;
//二进制来表示,
a:10      b:11     c:0
接下来就是分配空间了,在VS2022和VS2013我都测试过,都是先从低位开始分配
a占4个bit位,这里的而二进制位只有两位,所以高位补0。
a:0010 
b:00011 
c:0000000
                    
化为十六进制的形式为0x32,最后以%d的形式打印出来其实这里还涉及到了整型提升的知识,这里的i为int型,发生整型提升时高位补符号位00000000000000000000000000110010,因为符号位为0表示为正数,正数的原反补一致,所以最后得出的答案为50.
还有我看很多人的解析都提到了是从右向左分配空间是跟栈的内存分布规则有关,其实在位段中是与这没有一点关系的,从右往左也不是大小端的问题。在不同的编译器上可能会出现不同的结果和方式。


大部分人说的应该是下面这种情况:



编辑于 2022-07-29 11:02:32 回复(1)
我是这样理解的:
  a,b,c分别分配了4,5,7位空间;
  由栈的基本知识(高到低),栈上分配的空间顺序是:c,b,a
  因为a=1->0001;b=3->0 0011;c=0->0000 000(个数与分配的空间位数一致)
  所以全部总和起来:0000 0000 0011 0001(二进制)——>转化为十进制:50
发表于 2020-06-08 19:50:43 回复(0)
http://blog.csdn.net/liming0931/article/details/7721390   这个解析还不错,只是里面有一个小错误。里面的是5位,他只给了4位。
发表于 2015-10-12 10:43:22 回复(0)
前面体制这里看的懂,后面取地址有点搞不懂为什么会是0x00000032
发表于 2015-09-05 10:50:12 回复(0)
有两个知识点:一个是结构体的内存分配,一个是关于位域的内存对齐。 ********************************************** 1. 首先计算结构体所占内存大小。这里涉及到位域。注意,(1)如果相邻字段的类型相同,且其位宽之和小于该类型,则采用压缩存储,即后一字段紧跟着前一字段存储。(2)如果相邻字段的类型相同,且其位宽之和大于该类型,则第二个字段从该类型内存的整数倍开始存储。(3)如果相邻字段的类型不相同,看编译器,VC6采取不压缩方式,Dev-C++和GCC都采取压缩方式。由于abc都是无符号整数类型,位宽之和为16,刚好等于一个无符号整数类型的长度,属于情况(1),采用压缩存储。所以结构体一共占16位。 ********************************************** 2. 然后从栈中给结构体分配两个字节的内存,栈分配是优先高地址,然后往低地址走。分配完两个字节后,此时结构体的入口在低字节。因此从低地址开始按结构体成员定义顺序依次给结构体成员分配内存。(即给局部变量分配内存是分配在栈中,从高地址到低地址;而结构体成员的内存分配从低地址到高地址。)
编辑于 2019-04-06 08:33:35 回复(0)
自己做错是因为忘记了 栈的地址是由高向低增长,这个很关键,可是高赞答案里却没提到这个。
发表于 2019-03-11 16:54:03 回复(2)

When and where to use bitfield

  1. Linux 内核中常见;
  2. 《MISRA》—— Only used when storage space is at a premium.

    How to use

    “Almost everything about fields is implementation-dependent”

    《The C Programming Language》

programs using bit-field are not portable.

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

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

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

  • The order of allocation of bit-fields within a unit is implementation-defined

    (即:大端/小端)
    by (C99 标准 6.2.7.1)

  • 珍爱生命,慎用位域!

    除非你真的知道你在干嘛。

编辑于 2018-09-25 11:41:55 回复(0)
结构体里面的冒号表示位域,意思是分配多少空间
发表于 2018-03-05 23:39:31 回复(0)
根据申请内存的顺序,a,b,c的地址依次增加
 x86是32位的,小端字节序,转换成short型,
 低地址对应低位值,所以顺序是0,3,2(0000 000 0 0011 0010)
发表于 2016-05-07 20:42:36 回复(0)
从低地址到高地址,占用的两字节各位为:
0010 | 00011 | 0000000
  a     |    b      |       c

若是按照大端模式:
0010 0001 1000 0000 

              小端模式:
1000 0000 0010 0001

位操作没以字节为单位展开?
0000 000|0 0011| 0010  
编辑于 2022-12-30 14:33:07 回复(0)
考察 结构***段  和 整型提升   。。。。 细心就ok



这个题的为难之处呢,就在于前面定义结构体里面用到的冒号,如果你能理解这个符号的含义,那么问题就很好解决了。这里的冒号相当于分配几位空间,也即在定义结构体的时候,分配的成员a 4位的空间, b 5位,c 7位,一共是16位,正好两个字节。下面画一个简单的示意:
变量名  位数
test    15 14 13 12 11 10 9 |8 7 6 5 4 |3 2 1 0
test.a                      |          |0 0 1 0
test.b                      |0 0 0 1 1 |
test.c   0  0  0  0  0  0 0 |          |
在执行i=*((short *)&test); 时,取从地址&test开始两个字节(short占两个字节)的内容转化为short型数据,即为0x0032,再转为int型为0x00000032,即50
发表于 2021-06-09 20:53:10 回复(0)
低地址字节


高地址字节


x86是小段模式,LSB(最不关键的字节)存放在低地址,MSB(最关键的字节)存放在最高位。
大小端模式都是针对于字节而非位来说的,对于字节,顺序如平常书写顺序。
这种“:4”的写法,是结构***域(bit-fields)语法。
题目中的a,b,c按照内存的地址自然的从低往高分配。
分配结果如下:
低地址字节
b3b2b1b0
a3a2a1a0
高地址字节
c6c5c4c3
c2c1c0b4
a=a3a2a1a0=2=(0010)2
b=b4b3b2b1b0 =3=(00011)2
c=0
低地址字节(LSB)
0011
0010
高地址字节(MSB)
0000
0000
因而最后这一段内存中的数据视作short的时候为0x(0032)=50.

另外,纠正一下其它题解的一些错误。
1. 大端小端是针对字节而非位。
2. 这道题和栈的内存分配时是从高位到低位分配没有任何关系。

为什么说和栈的内存分配没关系。
因为首先test这个结构体是全局变量,占用的是堆空间而非栈空间。其次,即使test放在main函数内部声明,依旧和栈的内存分配没关系,因为test是一个struct mybitfields类型的结构体,栈分配内存地址的时候是作为一个整体,而test内部的内存布局依然是要遵循结构体成员变量的内存分布,和栈的内存分配没有关系。只有多个mybitfields结构体的时候才需要考虑这一点。
例如——
下面这个测试程序
	
	
	
#include <stdio.h>
#include <stdlib.h>
struct mybitfields {
unsigned short a : 4;
unsigned short b : 5;
unsigned short c : 7;
};
int k, i; /* 为了避免i,k干扰,我将k,i放到全局变量里.占用堆空间。*/
int main(void) {
struct mybitfields high, test, low;
/* high,test,low在栈中占用的空间由高到低。但是它们内部的空间占用仍然符合结构体的位域的规则。*/
test.a = 2;
test.b = 3;
test.c = 0;
high.a = 4;
high.b = 5;
high.c = 0;
low.a = 1;
low.b = 2;
low.c = 0;
i = *((short *)&test);
printf("short test: %08X\n", i);
k = *((int *)&test);
printf("int test: %08X\n", k);
k = *((int *)&high);
printf("int high: %08X\n", k);
k = *((int *)&low);
printf("int low: %08X\n", k);
}
 输出
short test: 00000032
int test: 00540032
int high: CCD00054
int low: 00320021
其中high由于在栈中的高地址(占据16位),更高的16位未知,所以输出的高16位是CCDD.

编辑于 2019-07-26 14:50:01 回复(0)
不应该是 00100001(低字节) 10000000(高字节)
发表于 2019-03-08 06:36:07 回复(0)