首页 > 试题广场 >

64位机上,一个结构体有三个成员,分别是char、int、s

[不定项选择题]
64位机上,一个结构体有三个成员,分别是char、int、short类型,三个成员位于结构体中不同位置时整个结构体的大小可能是下面哪些值?
  • 12
  • 7
  • 8
  • 16
三个数据,一共6种排列:
int 4B   char 1B   short 2B
根据内存对齐原则:
1. char int short   char1 + 空3 + int4 + short2 + 空2 = 12
2. char short int   char1 + 空1 + short2 + int4 = 8
3. int short char   int4 + short2 + char1 + 空1 = 8
4. int char short   int4 + char1 + 空1 + short2 = 8
5. short int char   short2 + 空2 + int4 + char1 + 空3 = 12
6. short char int   short2 + char1 + 空1 + int4 = 8

编辑于 2017-08-17 14:03:04 回复(10)
虽然开始我也认为这道题答案是有异议的,但是通过编译器编译发现确实是正确的。然后在网上找了一些资料,结合自己的实验给评论区各位疑惑“为毛是4字节不是8字节对齐”进行解答。
首先说一下实验平台,VS2017 X64的编译器,然后进行如下代码运行:
  1. #include <iostream>
  2. using namespace std;
  3. struct st1
  4. {
  5. char a ;
  6. int  b ;
  7. short c ;
  8. };
  9. struct st2
  10. {
  11. short c ;
  12. char  a ;
  13. int   b ;
  14. };
  15. int main()
  16. {
  17. cout<<"sizeof(st1) is "<<sizeof(st1)<<endl;
  18. cout<<"sizeof(st2) is "<<sizeof(st2)<<endl;
  19. return 0 ;
  20. }
得到的结果是 12  8。不用多说了,答案确实是正确的。那为什么它最后是按4字节对齐而不是8字节呢?
--------------------------------------------------------------------------------------------------------------------------------
内存对接法则:
我们这里只来讲没有#pragma pack(n)加持的情况。经过资料查阅,确实。对于32位编译器它默认的对齐方式是4,而64位默认对齐方式是8。但是如果大家去仔细阅读内存对齐的四条对齐法则,你会发现整体对齐法则是按照结构体内最大的内存对齐方式进行对齐的,而并没有说一定要按给定的对齐方法进行对齐。网上有一部分解释是说需要对齐的,那这里我就不清楚是不是不同的C++标准得到了不同的结果,我这里只有VS2017环境,所以我以此为平台,在X86(因为这样默认是4,大家不会有异议,而且用GCC可以重现)进行如下实验:
  1. #include <iostream>
  2. //#pragma pack(4) //注意,这里注释掉了,后续实验需要
  3. using namespace std;
  4. struct st1
  5. {
  6. char a;
  7. //double b;  //同样先注释掉
  8. };
  9. struct st2
  10. {
  11. char a;
  12. char b;
  13. };
  14. int main()
  15. {
  16. cout << "sizeof(st1) is " << sizeof(st1) << endl;
  17. cout << "sizeof(st2) is " << sizeof(st2) << endl;
  18. return 0;
  19. }
按照对齐方式,st1的大小应该是1(3)=4,而st2应该是2(2)=4。由于默认是4,本身不足4,补齐为4,没问题吧?但是运行结果大跌眼镜!!——1  2
看到没有,是按照1对齐的。
---------------------------------------------------------------------------------------------------------------------------------
关于pack:
网上的教程上说#pragma pack(n)是可以强制改变对齐方式的,是不是这样呢?经过试验,如果结构体内有大于n的值,它确实是可用的。如果没有,跟注释掉是一样的。所以网上很多人云亦云的一些说法是错误的。它们只是copy了别人的博客而没有亲自试验。还是上述代码,按照网上那些说法,如果去掉#pragma pack(4)的注释那结果应该是——4 4。进行实验,结果依然惊人!!——1  2
所以,网上说#pragma pack(n)可以强制改变对齐方式不是任何条件下的,是有条件的。如果没有大于n的数据,这句话是无效的。如果大家讲st1种double去掉,然后运行,会发现#pragma pack(4)效果是生效的。
-----------------------------------------------------------------------------------------------------------------------------------
实验结论:
经过上述实验,我们得到结论。
1.在内存对齐中,最后对齐是按照struct中最大的内存对齐方式对齐的,而并不会受默认对齐方式的影响。即默认对齐方式和编译器大小对于struct最后的大小是没有任何关系的。
2.在存在 pack的情况下, 如果有内存大小超过了默认对齐方式,默认方式才会起作用。例如,struct中如果存在double类型,而此时pack(4)存在,那最后struct大小是按4对齐而非8。但是如果没有超过4的,比如最大的是short,那最后的对齐方式是2而非4
3.网上许多关于默认对齐解释虽然结果与分析一致,只是因为巧合。例如有人说64位最后按8对齐,给出示例确实如此。并非其分析正确,而是他给的实例中含有double类型,一次变为8。
4.此题答案确实是BC,无任何异议。
-------------------------------------------------------------------------------------------------------------------------------------
最后说明:
再次强调,本人使用的实验平台是VS2017,不排除老的C++标准或者其他实验平台(如GCC)能得到本人的同样结论。这是本人原创,因此并不能保证逻辑一定正确或者实验一定无误。如果您有任何不同结论或者任何异议欢迎回复区讨论。谢谢个位!
编辑于 2018-01-18 10:45:11 回复(5)
https://levphy.github.io/2017/03/23/memory-alignment.html
内存对齐的3大规则:
  1. 对于结构体的各个成员,第一个成员的偏移量是0,排列在后面的成员其当前偏移量必须是当前成员类型的整数倍
  2. 结构体内所有数据成员各自内存对齐后,结构体本身还要进行一次内存对齐,保证整个结构体占用内存大小是结构体内最大数据成员的最小整数倍
  3. 如程序中有#pragma pack(n)预编译指令,则所有成员对齐以n字节为准(即偏移量是n的整数倍),不再考虑当前类型以及最大结构体内类型
编辑于 2017-09-12 10:07:57 回复(3)
发表于 2017-08-20 18:54:23 回复(0)
根据内存对齐原则:
A. 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是这个数据成员的自身长度的倍数。
B.在数据成员完成各自对齐之后,结构本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构最大数据成员长度中,比较小的那个进行。
int 4B   short 2B char 1B  ,一共6种组合:

1. char  int   short  ==》
第一步:进行数据成员对齐:
char占一个字节,起始偏移为int 4个字节,这个数据成员的自身长度= 4,所以int4字节对齐,起始偏移必须为4的倍数,所以起始偏移为4,在char后编译器会添加3个字节的额外字节,不存放任意数据。short2个字节,按2字节对齐,起始偏移为8,正好是2的倍数,无须添加额外字节。到此规则A的数据成员对齐结束,此时的内存状态为:

o x x x | o o o o | o o        x表示额外添加的字节)

0 1 2 3 4 5 6 7   8 9        (地址)


第二步:进行结构本身的对齐:
对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行,该结构中最大数据成员长度为int,占4字节,而默认的#pragma pack 指定的值为8,所以结果本身按照4字节对齐,结构总大小必须为4的倍数,需添加2个额外字节使结构的总大小为12 。此时的内存状态为:

o x x x | o o o o | o o x x    (x表示额外添加的字节)

0 1 2 3 4 5 6 7   8 9 a b   (地址)

==> char1 + 空3 + int4 + short2 + 空2 = 12, 共12字节

2. char  short int ==》 
 第一步:进行数据成员对齐
char占一个字节,起始偏移为0 ,short占2个字节,这个数据成员的自身长度= 2,所以short按2字节对齐,起始偏移必须为2的倍数,所以起始偏移为2,在char后编译器会添加1个字节的额外字节,不存放任意数据。int占4个字节,按4字节对齐,起始偏移为4,正好是2的倍数,无须添加额外字节。到此规则A的数据成员对齐结束,此时的内存状态为:

o x o o | o o o o |         (x表示额外添加的字节)

       0 1 2 3  4 5 6 7           (地址)
第二步:进行结构本身对齐
对齐将按照#pragma pack指定的数值和结构最大数据成员长度中,比较小的那个进行,该结构中最大数据成员长度为int,占4字节,而默认的#pragma pack 指定的值为8,所以结果本身按照4字节对齐,结构总大小必须为4的倍数,现在结构总大小为8,不需添加2额外字节。此时的内存状态为:

o x o o | o o o o |         (x表示额外添加的字节)

       0 1 2 3  4 5 6 7           (地址)

==>  char1 + 空1 + short2 + int4 = 8,共8个字节。

3. int  short  char==》 
第一步:进行数据成员对齐
int占4个字节,起始偏移为0 ,short占2个字节,这个数据成员的自身长度= 2,所以short按2字节对齐,起始偏移必须为2的倍数,所以起始偏移为4, char占1个字节,按1字节对齐,起始偏移为6,正好是2的倍数,无须添加额外字节。到此规则A的数据成员对齐结束,此时的内存状态为:

o o o o | o o o         (x表示额外添加的字节)

       0 1 2 3  4 5 6           (地址)
第二步:进行结构本身对齐
对齐将按照#pragma pack指定的数值和结构最大数据成员长度中,比较小的那个进行,该结构中最大数据成员长度为int,占4字节,而默认的#pragma pack 指定的值为8,所以结果本身按照4字节对齐,结构总大小必须为4的倍数,现在结构总大小为7,需添加1额外字节。此时的内存状态为:

o o o o | o o o x |         (x表示额外添加的字节)

       0 1 2 3  4 5 6 7           (地址)

 ==> int4 + short2 + char1 + 空1 = 8, 共8个字节。

4. int  char  short==》 
第一步:进行数据成员对齐
int占4个字节,起始偏移为0 ,char占1个字节,这个数据成员的自身长度= 1,所以char按1字节对齐,起始偏移必须为1的倍数,所以起始偏移为4, short占2个字节,按2字节对齐,起始偏移为6,正好是2的倍数,无须添加额外字节。char后编译器会添加1个字节的额外字节,到此规则A的数据成员对齐结束,此时的内存状态为:

o o o o | o x o o |        (x表示额外添加的字节)

       0 1 2 3  4 5 6 7         (地址)
第二步:进行结构本身对齐
对齐将按照#pragma pack指定的数值和结构最大数据成员长度中,比较小的那个进行,该结构中最大数据成员长度为int,占4字节,而默认的#pragma pack 指定的值为8,所以结果本身按照4字节对齐,结构总大小必须为4的倍数,现在结构总大小为8,不需添加额外字节。此时的内存状态为:

o o o o | o x o o |         (x表示额外添加的字节)

       0 1 2 3  4 5 6 7           (地址)

 ==> int4 + char1 + 空1 + short2 = 8, 共8个字节。

5. short  int  char==》 
第一步:进行数据成员对齐
short占2个字节,起始偏移为0 ,int占4个字节,这个数据成员的自身长度= 4,所以int按4字节对齐,起始偏移必须为4的倍数,所以起始偏移为4, char占1个字节,按1字节对齐,起始偏移为8,正好是2的倍数,无须添加额外字节。在short后编译器会添加2个字节的额外字节,到此规则A的数据成员对齐结束,此时的内存状态为:

o o x x | o o o o | o        (x表示额外添加的字节)

       0 1 2 3  4 5 6 7  8       (地址)
第二步:进行结构本身对齐
对齐将按照#pragma pack指定的数值和结构最大数据成员长度中,比较小的那个进行,该结构中最大数据成员长度为int,占4字节,而默认的#pragma pack 指定的值为8,所以结果本身按照4字节对齐,结构总大小必须为4的倍数,现在结构总大小为9,需添加3个额外字节。此时的内存状态为:

 o o x x | o o o o | o x  x  x       (x表示额外添加的字节)
 0 1 2 3  4 5 6 7  8 9  a  b    (地址)
 ==>  short2 + 空2 + int4 + char1 + 空3 = 12, 共12个字节。

6. short  char  int==》 
第一步:进行数据成员对齐
short占2个字节,起始偏移为0 ,char占1个字节,这个数据成员的自身长度= 1,所以char按1字节对齐,起始偏移必须为1的倍数,所以起始偏移为2, int占4个字节,按4字节对齐,起始偏移为4,正好是2的倍数,无须添加额外字节。在char后编译器会添加1个字节的额外字节,到此规则A的数据成员对齐结束,此时的内存状态为:

o o o x | o o o o |       (x表示额外添加的字节)

       0 1 2 3  4 5 6 7          (地址)
第二步:进行结构本身对齐
对齐将按照#pragma pack指定的数值和结构最大数据成员长度中,比较小的那个进行,该结构中最大数据成员长度为int,占4字节,而默认的#pragma pack 指定的值为8,所以结果本身按照4字节对齐,结构总大小必须为4的倍数,现在结构总大小为8,不需添加额外字节。此时的内存状态为:
 o o x x | o o o o |      (x表示额外添加的字节)
 0 1 2 3  4 5 6 7      (地址)

==>  short2 + char1 + 空1 + int4 = 8,共8个字节。
答案:12和8,即AC


发表于 2018-01-16 16:46:29 回复(0)
#pragma pack(1)当指定对齐大小的时候
struct st1
{
    char a;
    int b;
    short c;
};
这个时候结构体大小应该是7,所以这题答案有问题.
发表于 2020-01-18 15:52:58 回复(0)
默认内存对齐方式,按照数据结构中占用最大的那个元素的整数倍进行对齐。
char int short时,最大int,第一个是char,1字节,然后int,首字节不是4的整数倍,然后从4开始,4~7,然后是short,首字节是8,是2的整数倍,所以是8~9,最后10个字节再取一次4的整数倍,是12。
int short char,0~3 4~5 6~6,7个字节取8。
short int char 0~1, 4~7, 8~8, 9个字节取12.
char short int 0~1 2~3 4~8取8.
short char int 0~1 2~2 4~8 取8.
int char short 0~3 4~4 6~7 取8.
发表于 2019-03-01 15:32:42 回复(0)
不会C++,C都是大一学的,腾讯笔试里却大量充斥着这些
发表于 2017-09-13 13:16:52 回复(0)
不对吧,64位机器上,不应该按照8个字节存储嘛?char 1字节 int 4字节 short 2字节 所以一共8个字节啊
发表于 2017-08-16 22:08:26 回复(2)
。但是如果大家去仔细阅读内存对齐的四条对齐法则,你会发现整体对齐法则是按照结构体内最大的内存对齐方式进行对齐的,而并没有说一定要按给定的对齐方法进行对齐。 32位编译器的指针占用字节为4个字节,而64位编译器指针占用为8个字节。
发表于 2022-07-31 22:29:08 回复(0)
不用看了,跟编译器有关,我这里用ubuntu 1204 LTS 64位,gcc 64位编译,不管怎么调整成员的顺序,结果size都是8。遇到面试官出这种题目,我直接先说答案:和编译器有关,我的编译器永远是8,然后掏出手机给他看运行截图。
发表于 2020-07-30 17:35:10 回复(0)

32位编译器:

      char :1个字节 
      char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器) 
      short int : 2个字节 
      int:  4个字节 
      unsigned int : 4个字节 
      float:  4个字节 
      double:   8个字节 
      long:   4个字节 
      long long:  8个字节 
      unsigned long:  4个字节 

      64位编译器:

      char :1个字节 
      char*(即指针变量): 8个字节 
      short int : 2个字节 
      int:  4个字节 
      unsigned int : 4个字节 
      float:  4个字节 
      double:   8个字节 
      long:   8个字节 
      long long:  8个字节 
      unsigned long:  8个字节 

发表于 2017-09-06 15:18:41 回复(0)
个人感觉需要考虑数据在存储器中的存储方式,如果是从任意位置开始存储的话,他就是7。不一定必须边界对齐方式,要看计算机是怎么设计的。
发表于 2021-12-15 16:11:19 回复(0)
偏移量必须是自身长度的倍数 数据对齐之后结构体还要对齐,偏移量是最大字节的倍数
编辑于 2020-02-14 21:01:25 回复(0)
结构体要两次对齐 有#prama pack(n)时,以n为准
编辑于 2019-10-13 00:40:35 回复(0)
#pragma pack(1)
struct st
{
    char a;
    int  b;
    short c;
};
不知为何答案不能为7,但我在VS2017 X64验证出来就是 7,也许是我的编译器错了
编辑于 2019-04-12 09:46:18 回复(0)
struct align(16) ***
{
    int a;
    short b;
    char c;
};
cout<<sizeof(***)<<endl;//输出16

发表于 2019-03-17 08:28:55 回复(3)
有个疑问,这里说的是可能值,那么我#pragma pack(1),还请各路大神指点迷津
此时可能的大小就有7了
#pragma pack(1)
class  A
{
    char    a;
    short   b;
    int       c;
};

发表于 2018-03-08 15:59:59 回复(0)

《深入理解计算机系统》中写到 

32位 :linux下 2字节类型的数据的地址必须是2的倍数,4,8字节类型的数据的地址必须是4的倍数;,windows上  n个字节类型的数据的地址必须是n的倍数

x64-64位:linux和windows  都是 n个字节类型的数据的地址必须是n的倍数,如 long int ,double,指针都是8字节对齐,long double 16字节对齐

k字节对齐,则结构体总大小要是k的倍数,不够的末尾补齐

发表于 2017-09-13 09:43:41 回复(0)

#include <cstdint>

struct FuckYou {

  int8_t a;

  int16_t b;

  int32_t c;

} __attribute__((packed));

int main() {

  

  static_assert(sizeof(FuckYou) == 7, "");

  return 0;

}


发表于 2017-08-17 22:23:47 回复(0)