以便携方式检索传递给variadic函数的int32_t

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了以便携方式检索传递给variadic函数的int32_t相关的知识,希望对你有一定的参考价值。

7.16.1.1 2va_arg描述如下(强调我的):

如果没有实际的下一个参数,或者type与实际的下一个参数的类型不兼容(根据默认参数提升而提升),则行为是未定义的,除了以下情况:

  • 一种类型是有符号整数类型,另一种类型是相应的无符号整数类型,并且该值可在两种类型中表示;
  • 一种类型是指向void的指针,另一种是指向字符类型的指针。

现在我的理解似乎6.5.2.2(函数调用)与我没有矛盾,虽然我可能错了,默认促销是:

  • charintunsigned(指定实施)
  • signed charint
  • unsigned charunsigned
  • shortint
  • unsigned shortunsigned
  • floatdouble

当你知道传递给va_list的确切底层类型时,这一切都很好,花花公子(char除外,因为它的签名是实现指定的,所以AFAIK无法进行便携式检索)。

当你期望将<stdint.h>的类型传递给你的va_list时会变得更加复杂。

  • int8_tint16_t,通过逻辑极限观察得出,保证可以推广或已经是int类型。然而,依靠我原来的“逻辑”极限观察是非常可疑的,所以我正在寻求你(以及标准)对这个演绎的确认(我可能会遗漏一些我甚至都不知道的角落情况)。
  • 同样适用于uint8_tuint16_t,除了底层类型是unsigned
  • int32_t可能会或可能不会被提升为int。它可能比int大,小于或完全相同。同样适用于uint32_t,但适用于unsigned。如何便携式检索int32_tuint32_t传递给va_list?换句话说,如何确定int32_tuint32_t)是否已被提升为intunsigned)?换句话说,如何确定是否应该使用va_arg(va, int)va_arg(va, int32_t)来检索传递给可变函数的int32_t而不在任何平台上调用未定义的行为?
  • 我相信同样的问题对int64_tuint64_t有效。

这是一个理论上的(仅限标准的)问题,假设存在<stdint.h>中的所有精确宽度类型。我对“实践中的真实”类型的答案不感兴趣,因为我相信我已经了解它们。

编辑

我想到的一个想法是使用_Generic来确定int32_t的基本类型。我不确定你到底会怎样使用它。我正在寻找更好(更简单)的解决方案。

答案
#define IS_INT_OR_PROMOTED(X) _Generic((X)0 + (X)0, int: 1, default: 0)

用法:

int32_t x = IS_INT_OR_PROMOTED(int32_t) ? 
              (int32_t)va_arg(list, int) : 
              va_arg(list, int32_t);

在我的PC上使用gcc,宏为int8_tint16_tint32_t返回1,为int64_t返回0。

使用gcc-avr(一个16位目标),宏为int8_tint16_t返回1,为int32_tint64_t返回0。

对于long,无论是否sizeof(int)==sizeof(long),宏都返回0。

我没有任何64位ints的目标,但我不明白为什么它不适用于这样的目标。

我不确定这是否适用于真正的病态实施 实际上我很确定它现在适用于任何符合要求的实现。

另一答案

确实没有好办法做到这一点。我认为规范的答案是“不要这样做”。除了不将类型作为参数传递给可变函数之外,甚至避免将它们用作“变量”并仅将它们用作“存储”(在大量存在的数组和结构中)。当然很容易犯错并将这样的元素/成员作为参数传递给你的可变函数,所以这不是很令人满意。

使用_Generic的想法只有在这些类型没有使用您的代码不知道的特定于实现的扩展整数类型定义时才有效。

有一个可怕但有效的方法,包括使用正确的“PRI *”宏将va_list传递给vsnprintf,然后解析字符串中的整数,但是在执行此操作后,列表处于无法再次使用它的状态,所以如果仅适用于最终参数。

你最好的选择可能是试图找到一个公式“这种类型是否通过默认促销推广?”您可以轻松地查询该类型的最大值是否超过INT_MAXUINT_MAX,但如果存在具有相同范围的虚假扩展整数类型,则这仍然无法帮助形式正确。

另一答案

关于#if<limits.h>解决方案,我发现了这个(6.2.5.8):

对于具有相同签名和不同整数转换等级的任何两个整数类型(参见6.3.1.1),具有较小整数转换等级的类型的值范围是另一种类型的值的子范围。

6.3.3.1状态(强调我的):

每个整数类型都有一个整数转换等级,定义如下:

  • 没有两个有符号整数类型具有相同的等级,即使它们具有相同的表示。
  • 有符号整数类型的等级应大于精度较低的任何有符号整数类型的等级。
  • long long int的等级应大于long int的等级,该等级应大于int的等级,其应大于short int的等级,short rank应大于signed char的等级。
  • 任何无符号整数类型的等级应等于相应的有符号整数类型的等级(如果有的话)。
  • 任何标准整数类型的等级应大于具有相同宽度的任何扩展整数类型的等级。
  • char的等级应等于signed char和unsigned char的等级。
  • _Bool的等级应小于所有其他标准整数类型的等级。
  • 任何枚举类型的等级应等于兼容整数类型的等级(见6.7.2.2)。
  • 任何扩展有符号整数类型相对于具有相同精度的另一个扩展有符号整数类型的等级是实现定义的,但仍然受制于确定整数转换等级的其他规则。
  • 对于所有整数类型T1,T2和T3,如果T1具有比T2更大的秩并且T2具有比T3更大的秩,则T1具有比T3更大的秩。

这就是6​​.5.2.2 6所说的(强调我的):

如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将具有float类型的参数提升为double。这些被称为默认参数促销。如果参数数量不等于参数数量,则行为未定义。如果函数是使用包含原型的类型定义的,并且原型以省略号(,...)结尾,或者促销后的参数类型与参数类型不兼容,则行为未定义。如果使用不包含原型的类型定义函数,并且促销后的参数类型与促销后的参数类型不兼容,则行为未定义,但以下情况除外:

  • 一个提升类型是有符号整数类型,另一个提升类型是相应的无符号整数类型,并且该值可在两种类型中表示;
  • 这两种类型都是指向字符类型或void的限定或非限定版本的指针

基于这些观察,我会相信这一点

#if INT32_MAX < INT_MAX
    int32_t x = va_arg(va, int);
#else
    int32_t x = va_arg(va, int32_t);

这是因为如果int32_t的范围不能包含int的范围,那么int32_t的范围是int的子范围,这意味着int32_t的等级低于int的等级,这意味着整数提升是执行。

另一方面,如果int32_t的范围可以包含int的范围,那么int32_t的范围是int的范围或int范围的超集,因此int32_t的等级大于或等于int的等级#if INT32_MAX <= INT_MAX && INT32_MIN >= INT_MIN int32_t x = va_arg(va, int); #else int32_t x = va_arg(va, int32_t); ,表示不执行整数提升。

编辑

根据评论更正了测试。

int

编辑2:

我现在对这个案子特别感兴趣:

  • int32_t是32位的补码整数。
  • int是32位二进制补码整数(扩展类型)
  • 宽度(与精度相同?)是相同的
  • 但因为“任何标准整数类型的等级应大于具有相同宽度的任何扩展整数类型的等级。” int32_t的排名高于int32_t
  • 这意味着必须执行从intint的整数提升
  • 即使int32_t不能代表INT32_MIN中的所有值(具体而言,它不能代表qazxswpoi)会发生什么?或者我错过了什么?

以上是关于以便携方式检索传递给variadic函数的int32_t的主要内容,如果未能解决你的问题,请参考以下文章

3.8 Go语言中函数可变参数(Variadic Parameter)

我可以以任何方式从 OpenCV 对象取消引用中将数据分配给双重函数返回检索吗?

如何用 C 编写散列函数?

如何将数组从 c++ 传递给 python 函数并将 python 返回的数组检索到 c++

将 IOptions 传递给 .Net 核心中间件类以进行 json 配置检索

php pdo - 检索结果和行数 - 便携式解决方案