我在ideone.com上试过这段代码,它符合要求,但我无法解释发生了什么,任何帮助都会很好:)
代码如下:
#include <stdio.h>
typedef union {
unsigned char* g_pointer;
struct {
unsigned short local_addr;
unsigned char globle_page;
} g_l;
} Gld_WordType;
int main()
{
int Idx;
Gld_WordType test;
test.g_l.globle_page = 0x13;
test.g_l.local_addr = 0xfff0;
printf("g_pointer: %x\n
local_addr: %x\n
globle_page: %x\n",
test.g_pointer,
test.g_l.local_addr,
test.g_l.globle_page);
test.g_pointer++;
printf("g_pointer: %x\n
local_addr: %x\n
globle_page: %x\n",
test.g_pointer,
test.g_l.local_addr,
test.g_l.globle_page);
return 0;
}
结果是:
g_pointer: 13fff0
local_addr: fff0
globle_page: 13
g_pointer: 13fff1
local_addr: fff1
globle_page: 13
如果我简单地切换
local_addr
和globle_page
的顺序,结果会有所不同:typedef union {
unsigned char* g_pointer;
struct {
unsigned char globle_page; // Changed order here.
unsigned short local_addr; // And here
} g_l;
} Gld_WordType;
这次,结果是:
g_pointer: fff00013
local_addr: fff0
globle_page: 13
g_pointer: fff00014
local_addr: fff0
globle_page: 14
好的,这是我到现在为止对这个问题的理解,如果有什么问题,请指出。
第一,在第一个例子中,union是这样组成的(前面的
local_addr
)############ #############
# # # #
# g_pointer# # local_addr#
# # # MSB or LSB#
############ #############
#############
# #
# local_addr#
# MSB or LSB#
#############
##############
# #
# globle_page#
# #
##############
如果
globle_page
是在globle_page
之前定义的,则布局如下:############ ##############
# # # #
# g_pointer# # globle_page#
# # # #
############ ##############
##############
# #
# local_addr #
# MSB or LSB #
##############
##############
# #
# local_addr #
# MSB or LSB #
##############
因此,在情况1(
local_addr
定义在local_addr
之前)中,如果globle_page
的值更改,g_pointer
的MSB或LSB也将更改,但为什么它实际上要添加一个呢?因为我知道我的平台是大端字节顺序,所以应该更改local_addr
的MSB,为什么要更改LSB?在第二种情况下(
local_addr
在globle_page
之前定义),我可以解释local_addr
的值加了一个,而对应的“globle_page”地址也加了一个,但是由于第一种情况,我对此不太确定。有人能告诉我这里发生的事情的确切答案吗?如果我没有恰当地描述这个问题,我的英语很差。
顺便说一下,我使用的平台没有字节对齐问题。所以struct是按写的顺序逐字节布局的。
谨致问候,
盛运
最佳答案
could anyone tell me the exact answer of what is happening here?
首先,也是最重要的是要知道,结果是未定义的行为。对于不同的编译器,甚至对于使用不同选项的同一个编译器,您可能会看到不同的结果。最常见的替代行为是,当您修改一个字段时,其他字段直到将来某个时间才会更改。我以前写过一些测试代码
// assign to the first field
// print the second field
// print the second field
第一次打印显示第二个字段的前一个值,第二次打印显示第二个字段的更新值。
在您真正了解有关别名和未定义行为的操作之前,不应以这种方式使用
union
s。接下来要知道的是指针很少有1字节长。在大多数现代机器上,它们往往是4或8字节。
关于数据布局,接下来要知道的是有时会填充
structs
。对于结构struct {
char a;
short b;
};
我认为布局很可能是
<one byte of a> <unused byte> <two bytes of b>
作为
<one byte of a> <two bytes of b>
我会毫不惊讶地看到
<one byte of a> <3 unused bytes> <two bytes of b>
事实上,根据你的经验结果,我认为你有4字节的指针,最后一个可能性是
struct
是如何实际布局的。您可以使用
sizeof
和offsetof
函数精确地确定事情。sizeof
将告诉您每种类型的长度为多少字节,offsetof
将让您确定结构或联合中每个字段的起始位置。需要注意的另一点是指针的布局并不总是像整数一样。此问题取决于您运行的计算机体系结构。不过,我认为所有“普通”的人都会像你期望的那样。