我在VS2013中遇到了一个与printf不同的问题,我在下面的小测试案例中已经将其隔离了。
使用x64编译器构建时一切都很好。当为32位构建时,其中一个变量打印不正确。无论是在Visual Studio内部构建还是在命令行使用cl,都会发生这种情况。它也会在禁用优化的情况下发生。
问题是printf中的十六进制格式说明符(或可能是UINT64)。当我从调用中删除格式说明符和变量时,它按预期工作。这与分割对printf的调用相同,它也可以正常工作。
显示不当行为的命令行输出如下。请注意content_layout
的打印方式不同,首先为零(错误),然后为一(正确)。
我的问题 - 我在代码中做错了什么,或者编译器在x86上产生了错误的输出?
从cl输出并运行程序:
## Compile, disable optimisation with /Od
C:\Path\TestStruct>cl /Od main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.40629 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
Microsoft (R) Incremental Linker Version 12.00.40629.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
main.obj
C:\Path\TestStruct>main.exe
## This line is incorrect and prints zero for content_layout
handoff_size: 29336, handoff_location: 0xc8ff8000, content_layout: 0, content_type: 1
## These two lines are correct and print one for content_layout
## Note these are split up into two calls to printf()
handoff size: 29336, handoff_location: 0xc8ff8000
content_layout: 1, content_type: 1
测试用例代码如下:
#include <Windows.h>
#include <stdio.h>
// Don't allow compiler to align structs - pack members on byte boundary
#pragma pack(1)
typedef struct _acpi_header {
char signature[4];
UINT32 length;
UINT8 revision;
UINT8 checksum;
char oem_id[6];
char oem_table_id[8];
UINT32 oem_revision;
UINT32 creator_id;
UINT32 creator_revision;
} acpi_header_t;
typedef struct _wpbt_header {
UINT32 handoff_size;
UINT64 handoff_location;
UINT8 content_layout;
UINT8 content_type;
} wpbt_header_t;
int main(void) {
acpi_header_t *acpi_header;
wpbt_header_t *wpbt_header;
BYTE lpBuf[] = {
0x57, 0x50, 0x42, 0x54, 0x38, 0x00, 0x00, 0x00, 0x01, 0xe4, 0x41, 0x42, 0x54, 0x2d, 0x4e, 0x54,
0x41, 0x42, 0x54, 0x2d, 0x57, 0x50, 0x42, 0x54, 0x01, 0x00, 0x00, 0x00, 0x41, 0x42, 0x54, 0x57,
0x02, 0x04, 0x12, 0x20, 0x98, 0x72, 0x00, 0x00, 0x00, 0x80, 0xff, 0xc8, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00
};
acpi_header = (acpi_header_t*)(lpBuf);
wpbt_header = (wpbt_header_t*)(lpBuf + sizeof(acpi_header_t));
// Does not work - content_layout is printed as 0 (zero)
printf("handoff_size: %lu, handoff_location: 0x%x, content_layout: %u, content_type: %u\r\n", wpbt_header->handoff_size,
wpbt_header->handoff_location, wpbt_header->content_layout, wpbt_header->content_type);
// Works - content_layout is printed as 1
printf("handoff size: %lu, handoff_location: 0x%x\r\n", wpbt_header->handoff_size, wpbt_header->handoff_location);
printf("content_layout: %u, content_type: %u", wpbt_header->content_layout, wpbt_header->content_type);
getchar();
return 0;
}
答案 0 :(得分:2)
您使用错误的printf
格式打印单个字节的问题。
如果您按照链接的参考并检查"%u"
格式,您会看到没有任何前缀,它会打印unsigned int
而不是UINT8
(可能是unsigned char
})。 unsigned char
的正确格式为"%hhu"
。
但是,C已经标准化了一组predefined format macros,您应该使用它来代替固定宽度类型。要打印无符号的8位字节,应使用PRIu8
:
printf("content_layout: %" PRIu8 ", content_type: %" PRIu8 "\n",
wpbt_header->content_layout, wpbt_header->content_type);
答案 1 :(得分:2)
handoff_location
的格式说明符不正确。 %x
用于int
,在x86版本的代码中只有32位。但是你正在将64位int推入堆栈,这意味着,当printf处理它时,它会留下&#34; hand_off_location
的前四个字节,并将它们解释为content_layout
。反过来,content_layout
实际上被解释为content_type
,content_type
被忽略。
我要做的是使用%llx
并将handoff_location
投射到unsigned long long
。
另一点 - 在这种情况下你似乎已经逃脱了 - 是编译器可以自由地在结构的成员之间放置填充字节。你永远不应该只是将一个结构体叠加到一个二进制缓冲区上,并希望它有效。
64位版本可能有三个原因之一:
int
是64位宽