【问题标题】:is an argument of type long OK for %x printf specifier?是 %x printf 说明符的 long OK 类型的参数吗?
【发布时间】:2020-08-17 15:30:19
【问题描述】:

我在Win32 docs 上找到了这个:

// Check the smart card context handle.
// hContext was set previously by SCardEstablishContext.

LONG    lReturn;
lReturn = SCardIsValidContext(hContext);
if ( SCARD_S_SUCCESS != lReturn )
{
   // Function failed; check return value.
   if ( ERROR_INVALID_HANDLE == lReturn )
       printf("Handle is invalid\n");
   else
   {
       // Some unexpected error occurred; report and bail out.
       printf("Failed SCardIsValidContext - %x\n", lReturn);
       exit(1);  // Or other appropriate error action.
   }
}
else
{
   // Handle is valid; proceed as needed.
   // ...
}

printf("Failed SCardIsValidContext - %x\n", lReturn); 行将LONG 类型的参数(long 的类型定义)传递给printf,其中printf 期望unsigned int 根据cppreference.com。这是明确定义的行为吗?如果是这样,它是否与显式 static_castunsigned int 相同?

【问题讨论】:

    标签: c++ printf variadic-functions


    【解决方案1】:

    对于 %x printf 说明符,是否是 long 类型的参数?

    不,在 C++ 中不行。 %x 用于 unsigned int,而不是 long。

    这是明确定义的行为吗?

    不,这是未定义的行为。引用 C 标准草案(定义格式说明符的地方):

    x,X unsigned int argument 被转换为 ... 无符号十六进制表示法 ...

    如果任何参数不是相应转换规范的正确类型,则行为未定义。


    如果系统文档是这样的,在windows中可能是“OK”,但是依靠这样的保证将不能移植到其他系统。我怀疑这个例子并不是为了证明这种滥用格式说明符的有效性,而是偶然的。

    【讨论】:

    • 这是标准字母 (+1) 的正确答案,虽然这本质上是说 int n; unsigned k = *(unsigned *)&n; 是 UB,我觉得这有点令人惊讶。
    • @dxiv 它没有说明将有符号整数重新解释为无符号整数,这是明确定义的。
    • 是的,但是printf 可能会在遇到%x 时执行类似unsigned k = va_arg(args, unsigned) 的操作,并且我所知道的所有实现都将其转换为k = *(unsigned *)(void *)args。所以最后,UB 正在从写入int 的地址读取unsigned
    【解决方案2】:

    printf("%x", (LONG)101); 有效且定义明确在 Windows 中,其中 LONGdefined 是“32 位有符号整数”,与 @987654326 相同@ 和 unsigned int 的大小相同。

    使用 C++ 类型的可移植等效项是 printf("%lx", (unsigned long)101l);

    【讨论】:

    • %x 是否有效,或%lx 是否有效?
    • @Ayxan Both,考虑到 Windows 如何定义类型,预计不会很快改变,因为它会破坏整个 Win32 API。
    • 有趣。我问这个是因为 cppreference 说 “如果 default conversions 之后的任何参数不是相应转换说明符所期望的类型,或者如果参数少于格式所需的参数,则行为未定义。”虽然大小匹配,但类型仍然不匹配。所以,我不确定。也许我错过了什么。
    • @Ayxan 在printf 中使用"%x"signed int 确实是UB,正如this answer 中所述(对于C,Win32 API 正在使用)。我不知道有任何 Windows 编译器 not 使用这种构造实现预期的效果,但从技术上讲,它仍然是一种语言违规。我想像printf("%x", lReturn + 0ul); 这样的东西可以解决这个问题。
    猜你喜欢
    • 2020-10-07
    • 1970-01-01
    • 1970-01-01
    • 2020-05-23
    • 1970-01-01
    • 2020-08-27
    • 1970-01-01
    • 2016-05-29
    • 2018-03-30
    相关资源
    最近更新 更多