如何将字符串可移植转换为不常见的整数类型?

一些背景:如果我想用,例如, scanf()将字符串转换为标准整数类型,如uint16_t ,我将使用 SCNu16 ,如下所示:

 #include  #include  uint16_t x; char *xs = "17"; sscanf(xs, "%" SCNu16, &x); 

但是像pid_t这样的更不常见的整数类型没有任何这样的东西; 仅支持普通的整数类型。 要转换为另一种方式,可移植printf()一个pid_t ,我可以将它转换为intmax_t并使用PRIdMAX ,如下所示:

 #include  #include  #include  pid_t x = 17; printf("%" PRIdMAX, (intmax_t)x); 

但是,似乎没有办法将scan scanf()移植到pid_t 。 所以这是我的问题:如何便携?

 #include  #include  pid_t x; char *xs = 17; sscanf(xs, "%u", &x); /* Not portable! pid_t might not be int! /* 

我想到scanf()转到intmax_t ,然后在转换为pid_t之前检查该值是否在pid_t的限制范围内,但似乎没有办法获得pid_t的最大值或最小值。

有一个强大的便携式解决方案,即使用strtoimax()并检查溢出。

也就是说,我解析一个intmax_t ,检查来自strtoimax()的错误,然后通过强制转换它并将其与原始的intmax_t值进行比较来查看它是否“适合”在intmax_t

 #include  #include  #include  #include  char *xs = "17"; /* The string to convert */ intmax_t xmax; char *tmp; pid_t x; /* Target variable */ errno = 0; xmax = strtoimax(xs, &tmp, 10); if(errno != 0 or tmp == xs or *tmp != '\0' or xmax != (pid_t)xmax){ fprintf(stderr, "Bad PID!\n"); } else { x = (pid_t)xmax; ... } 

不可能使用scanf() ,因为(正如我在评论中所说) scanf()不会检测溢出 。 但我说错了, strtoll()相关的函数都没有采用intmax_t ; strtoimax()呢!

除非你知道整数类型的大小(在这种情况下为strtoimax() ,否则除了strtoimax()之外还使用其他任何东西strtoimax()

这取决于你想要的便携性。 POSIX说pid_t是一个有符号整数类型,用于存储进程ID和进程组ID。 在实践中,你可以安全地假设long足够大。 如果失败了,你的intmax_t必须足够大(所以它会接受任何有效的pid_t ); 麻烦的是,该类型可以接受pid_t中不合法的值。 你被困在一块岩石和一块坚硬的地方之间。

我会用long而不用担心它,除了100年代的软件考古学家会发现并观察到的一个模糊的评论给出了一个原因,为什么256位CPU在递给512位值时嘎然而止作为一个pid_t

POSIX 1003.1-2008现已在网上提供(所有3872页,PDF和HTML)。 你必须注册(免费)。 我是从Open Group书店那里得到的。

我所看到的只是它必须是有符号整数类型。 显然,所有有效的有符号整数值都适合intmax_t 。 我在中找不到任何指示PID_T_MAX或PID_T_MIN或其他此类值的信息(但我只是在今晚才能访问它,所以它可能隐藏在我没有的地方寻找它)。 OTOH,我支持我的原始评论 – 我相信32位值是实用的,而且无论如何我会使用long ,这在8位机器上是64位。 我认为可能发生的最糟糕的事情是“适当特权”进程读取的值太大,并且由于类型不匹配而向错误的进程发送信号。 我不相信我会担心这一点。

…… oooh!… p400在

该实现应支持一个或多个编程环境,其中blksize_t,pid_t,size_t,ssize_t和suseconds_t的宽度不大于long类型的宽度。

如果你真的担心,你可以_assert(sizeof(pid_t) <= long)或你为'%'选择的任何类型。

正如在这个答案中解释的那样,规范称为signed int 。 如果'int'发生变化,你的'%u'按定义会随之改变。