c语言 - 如何构造一个与 scanf( "%zn" ) 一起使用的"signed size_t"?



我尝试使用以下程序获取作为size_t读取的字符数:

#include <stdio.h>
int main(void)
{
size_t i;
sscanf("abc", "%*s%zn", &i);
printf("%zu", i);
}

GCC 12对此发出警告:

scanf.c: In function ‘main’:
scanf.c:7:25: warning: format ‘%zn’ expects argument of type ‘signed size_t *’, but argument 3 has type ‘size_t *’ {aka ‘long unsigned int *’} [-Wformat=]
7 |     sscanf("abc", "%*s%zn", &i);
|                       ~~^   ~~
|                         |   |
|                         |   size_t * {aka long unsigned int *}
|                         long int *
|                       %ln

这样做是正确的²;在C17标准草案第234页中,我们阅读了(我的重点)

不消耗任何输入。相应的参数应是指向有符号整数的指针,该整数将被写入该fscanf函数调用迄今为止从输入流读取的字符数。

早期标准包含类似的措辞。

那么,我如何(便携地)为这个转换创建一个size_t的签名等价物呢?

在C++中,我可以使用std::make_signed_t<std::size_t>,但这显然不是C代码的选项。如果没有这一点,%zn转换在C.中似乎是不可用的


出现这种情况的真实世界案例来自于审查简单的光照发生器,我们想要更通用的strto𝑥()形式,因此需要%n来确定转换的结束。我知道我们可以在这里对所有的𝑥使用普通的int,但希望在报告为bug之前检查预期的行为。

²除了调用所需的类型signed size_t *之外,这显然不是一个有效的C类型名称。

如何构造一个"带符号的size_t";用于scanf("%zn")

你运气不好。自C17 起

z指定后面的d、i、o、u、x、x或n转换说明符应用于类型指针指向size_t对应的带符号整数类型的参数。C17dr§7.21.6.2 11

n不消耗任何输入。相应的参数应是指向带符号整数的指针。。。。C17dr§7.21.6.2 12

并且size_t是一个无符号类型。

然而,C从未详细说明如何使对应的有符号整数类型

标准C中没有指定与size_t对应的签名类型


替代方案:

  • 先使用"%n", &int_object,然后使用size_t i = (unsigned) int_object;。(铸造重要)

  • 使用"%jn", &intmax_t_object,然后使用size_t i = (size_t) intmax_t_object;


如果按下以键入signed_size_t,则以下应可移植,但您可以自己操作。

#include <assert.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#if SIZE_MAX == UINT_MAX
typedef int signed_size_t;
#elif SIZE_MAX == ULONG_MAX
typedef long signed_size_t;
#elif SIZE_MAX == ULLONG_MAX
typedef long long signed_size_t;
#elif SIZE_MAX == UINTMAX_MAX;
typedef intmax_t signed_size_t;
#elif SIZE_MAX == USHRT_MAX
typedef short signed_size_t;
#else
#error Strange `size_t`.
#endif
_Static_assert(sizeof(size_t) == sizeof(signed_size_t), "Strange size_t");

关于非标准ssize_t

ssize_t对应的有符号整数类型不一定匹配。

即使是2018年第7期开放式集团基础规范也有:

ssize_t
这是size_t的有符号模拟。措辞是这样的,实现可以选择使用更长的类型,也可以简单地使用size_t的签名版本。。。


类似的问题如何使用";zd";带有printf()?的说明符?。

"签名CCD_ 28";无论是在POSIX还是C标准的任何版本中,都不是真正作为标准定义的类型存在的。

POSIXssize_t被指定为";ssize_t型应能够存储至少在[-1, {SSIZE_MAX}]范围内的值&";,因此即使ssize_t可用;签署的CCD_ 33";价值

虽然没有一种类型可以保证足够大以容纳";有符号的CCD_ 34";严格一致性代码中的值类型,在我所知道的所有平台上,标准类型long long int似乎是最";未来证明";。(size_t大于unsigned long long int的几率可能在零到无穷小之间,但我不认为任何版本的C标准都排除了这一点。)

在我写这篇文章的时候,据我所知,没有一个平台使用大于64位的size_t值,而且long long int保证至少是64位,所以它足够大。

int64_t似乎是一个不错的选择,但如果系统的size_t大于64位,int64_t就会失败。我怀疑size_t大于64位的任何系统实现都可能扩展long long int以匹配。

你可以随时在代码中添加这样的内容来保护自己

#if SIZE_MAX > ULLONG_MAX
#error size_t larger than unsigned long long int
#endif

尽管使用" %lld ..."格式说明符扫描long long int比使用" " SCNd64 " ..."宏扫描int64_t更容易。。。

如果你真的想创建一个";匹配有符号的CCD_ 49";类型,您可以在#if梯形图中扩展使用SIZE_MAX,将其与各种U*MAX值进行比较,以找到";最正确的";匹配要在CCD_ 53中使用的带符号整数类型。但是,您必须创建自己的scanf()(可能还有printf()宏)。

或者

或者,您可以在POSIX系统上使用ssize_t,在非POSIX系统中创建自己的ssize_t。IME没有CCD_ 58实现不事实上充当";签名CCD_ 59";价值

相关内容

  • 没有找到相关文章

最新更新