这个 DWORD 相关代码是未定义行为吗?
Is this DWORD related code undefined behaviour?
这是我第三次尝试澄清我对这个话题的困惑。但是这次我有不同的问题。
我有这个代码
DWORD v1, v2, v3, Build;
GetVersion(&v1, &v2, &v3, &Build);
sprintf(VersionStr, "%d.%d.%d.%d", v1, v2, v3, Build);
大概是 10 年前使用 Visual Studio 编写的。我知道 DWORD
总是 unsigned
-这是真的吗?
现在,here,其中一个答案引用了某个版本的标准(这个标准版本是否适用于我的代码?)它说的是 va_arg
:
The standard isn't 100% clear on this point. On one hand, you get the
specification for va_arg, which says (§7.15.1.1/2):
If there is no actual next argument, or if type is not compatible with
the type of the actual next argument (as promoted according to the
default argument promotions), the behavior is undefined, except for
the following cases:
one type is a signed integer type, the other type is the
corresponding unsigned integer type, and the value is representable in
both types;
one type is pointer to void and the other is a pointer to a character
type.
另一方面,那个答案也说了 printf
On the other hand, you get the specification of printf (§7.19.6.1/9):
If any argument is not the correct type for the corresponding
conversion specification, the behavior is undefined."
所以他首先引用了关于 va_arg
的引述,然后引用了关于 printf
的引述。看来他也不清楚。另一个答案提到尽管有 va_arg
文档,但无论如何这都违反了 printf
合同。请看线程。我很困惑。
所以我的问题基本上是我在任何情况下都呈现未定义行为的代码?或者例如 对于可以在 int
中表示的 v1
的值,这不是未定义的行为(如我引用的其中一个答案所述)?
也可能是因为我的代码很旧,可能是因为旧版本的标准不是未定义的行为?
这真的很简单:sprintf
中的格式说明符必须 匹配传递的参数类型。请不要试图推测此规则的例外情况。
由于 DWORD 是 unsigned long
而 %d
不是 unsigned long
的正确格式说明符,您的程序的行为未定义。您必须 使用%lu
。由于 DWORD
不是标准类型,您应该包含一行
static_assert(std::is_same<DWORD, unsigned long>::value, "DWORD is not an unsigned long");
在你程序的某处。
DWORD
定义为无符号 32 位整数 [MSDN].
unsigned long
保证至少是 32 位无符号整数 [cppreference].
因此,您始终可以将 DWORD
分配给 unsigned long
而不会损失精度,因此您可以将 unsigned long 说明符与 printf
等一起使用将参数转换为 unsigned long。
替代方法是使用保证等于 DWORD
的 std::uint32_t
并在 printf
.
中使用该格式说明符
这是我第三次尝试澄清我对这个话题的困惑。但是这次我有不同的问题。
我有这个代码
DWORD v1, v2, v3, Build;
GetVersion(&v1, &v2, &v3, &Build);
sprintf(VersionStr, "%d.%d.%d.%d", v1, v2, v3, Build);
大概是 10 年前使用 Visual Studio 编写的。我知道 DWORD
总是 unsigned
-这是真的吗?
现在,here,其中一个答案引用了某个版本的标准(这个标准版本是否适用于我的代码?)它说的是 va_arg
:
The standard isn't 100% clear on this point. On one hand, you get the specification for va_arg, which says (§7.15.1.1/2):
If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:
one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;
one type is pointer to void and the other is a pointer to a character type.
另一方面,那个答案也说了 printf
On the other hand, you get the specification of printf (§7.19.6.1/9):
If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined."
所以他首先引用了关于 va_arg
的引述,然后引用了关于 printf
的引述。看来他也不清楚。另一个答案提到尽管有 va_arg
文档,但无论如何这都违反了 printf
合同。请看线程。我很困惑。
所以我的问题基本上是我在任何情况下都呈现未定义行为的代码?或者例如 对于可以在 int
中表示的 v1
的值,这不是未定义的行为(如我引用的其中一个答案所述)?
也可能是因为我的代码很旧,可能是因为旧版本的标准不是未定义的行为?
这真的很简单:sprintf
中的格式说明符必须 匹配传递的参数类型。请不要试图推测此规则的例外情况。
由于 DWORD 是 unsigned long
而 %d
不是 unsigned long
的正确格式说明符,您的程序的行为未定义。您必须 使用%lu
。由于 DWORD
不是标准类型,您应该包含一行
static_assert(std::is_same<DWORD, unsigned long>::value, "DWORD is not an unsigned long");
在你程序的某处。
DWORD
定义为无符号 32 位整数 [MSDN].
unsigned long
保证至少是 32 位无符号整数 [cppreference].
因此,您始终可以将 DWORD
分配给 unsigned long
而不会损失精度,因此您可以将 unsigned long 说明符与 printf
等一起使用将参数转换为 unsigned long。
替代方法是使用保证等于 DWORD
的 std::uint32_t
并在 printf
.