C语言 如何在没有错误的情况下检查 strtol 溢出?



给定以下示例,该示例分析仅包含有效数字字符的字符串,但对于类型long int[0] 来说很大。

char *endptr = NULL;
long int val = strtol("0xfffffffffffffffffffffffff", &endptr, 0);

据我了解,捕获此错误的唯一方法是检查是否设置了errno[1]。 尽管errno应该是线程安全的[2],但在大多数实时嵌入式系统[3]中,情况并非如此,其中errno是全局int- 这是正确的吗?在这种情况下,errno不能被信任,因为它可能是从中断[4]上下文中修改的。

有没有办法在不使用errno的情况下捕获此错误,或者有其他解决方法?实现自定义strto*似乎不是理智的解决方案,但也许没有其他方法?

[0] 类似的例子可以构造strto*族中的其他函数,如strtodstrtofstrtoldstrtollstrtoulstrtoull

[1] http://man7.org/linux/man-pages/man3/strtol.3.html

[2] errno 线程安全吗?(也是相关问题(

[3] 在"裸机"项目或低级RTOS(如Apache mynewt,Zephyr或FreeRTOS(中的示例。

[4] 根据中断或操作系统调度程序可能提供的其他上下文进行修改。我相信这些通常只管理堆栈,仅此而已。

如何在没有错误的情况下检查strto...()溢出?

首先,我怀疑OP的断言:"在大多数实时嵌入式系统中,errno是全局整数的大多数实时嵌入式系统[2]并非如此 - 这是正确的吗?">尽管如此,这里有一些想法。

  1. 对于浮点 (FP(,重写strto...()容易失败。如果您的 FP 支持无穷大,只需使用isinf()来检测溢出 - 即使输入字符串是"无限" - 或测试此类无穷大字符串。

接近 0.0 的小值溢出是strto...()问题的兔子洞。 我假设 OP 不是在那里寻找溢出

  1. 对于整数strto...(),重写并不难。 很容易找到好的源代码。 如果仍然想避免签名strto...(),请调用strtol()strtoul()并比较结果。 函数中的溢出返回不比较的结果。 也适用于像"-123"这样的long范围内的负片。 对于未签名strto...()- 必须考虑这一点。

    bool strtol_no_errno(const char *s, long *num( { *num = strtol(s, NULL, 0(; 无符号长 unum = strtoul(s, NULL, 0(; 返回 unum == (无符号长( *数字; }

  2. 对于整数strto(u)l(),当long小于long long时,只需调用strto(u)ll()并测试结果是否在范围内。

<小时 />

测试

#include <stdbool.h>
#include <stdio.h>
int main( ) {
char buf[100] = "-1";
for (int i = 0; i<21; i++) {
long num;
bool ok = strtol_no_errno(buf + 1, &num);
printf("%30s %d %ldn", buf + 1, ok, num);
ok = strtol_no_errno(buf + 0, &num);
printf("%30s %d %ldn", buf, ok, num);
strcat(buf, "0");
}
}

输出

1 1 1
-1 1 -1
10 1 10
-10 1 -10
...
100000000000000000 1 100000000000000000
-100000000000000000 1 -100000000000000000
1000000000000000000 1 1000000000000000000
-1000000000000000000 1 -1000000000000000000
10000000000000000000 0 9223372036854775807
-10000000000000000000 0 -9223372036854775808
100000000000000000000 0 9223372036854775807
-100000000000000000000 0 -9223372036854775808

您应该做的第一件事是清理输入。例如,如果没有"0x"的字符串长度大于sizeof(long)*2,则输入显然太大。因此,首先要验证这一点。

还要确保如果输入的字符串长度正好为sizeof(long)*2,则对于有符号longinput[0]必须'7'或更小。或者,如果input[0]'-',请检查input[1]。(这里要考虑一些 2 的补充技巧,我会留给你。

通过上述健全性检查,应该不可能溢出。但如果这样做,该函数保证返回LONG_MAX(或LONG_MIN为下溢(。此外,endptr设置为输入字符串的开头,以防出现此类错误。因此,您可以像这样检查转换是否失败:

if(val != LONG_MAX) 
{ 
/* OK, normal case */ 
}
else // val == LONG_MAX
{
if(endptr != input &&
(size_t)(endptr-input) == sizeof(long)*2))
{
/* OK, input was LONG_MAX but no overflow */ 
}
else
{
/* overflow error */
}
}

假设input没有0x前缀 - 如果有,则必须相应地调整代码。

下溢需要相同的检查。

相关内容

  • 没有找到相关文章

最新更新