如何将字符串转换为存储在整数数组中的值?
问题的出现原因是因为在将字符串转换为整数数组时,使用了不可靠且几乎无用的ato...
函数组。虽然稍微好一些的解决方法是使用sscanf
函数,但它也并不完美。
要将字符串转换为整数,应该使用strto...
函数组中的函数。在这种情况下,应该使用strtol
函数。
实际上,如果sscanf
尝试转换超出其类型范围的数字(例如,sscanf("999999999999999999999", "%d", &n)
),它会产生未定义的行为。
Thompson: 这正是我的意思。 atoi
没有提供有意义的成功/失败反馈,并且在溢出时具有未定义的行为。 sscanf
在溢出时提供了某种程度的成功/失败反馈(即返回值,这使其“稍微好一些”),但仍然具有溢出时的未定义行为。只有strtol
是可行的解决方案。
同意;我只是想强调sscanf
可能致命的问题。(尽管我承认我有时会使用atoi
,通常是用于我不希望源代码存活超过10分钟的程序,然后删除源代码。)
文章整理如下:
问题的出现原因是因为在将字符串转换为整数数组时,使用了不可靠且几乎无用的ato...
函数组。虽然稍微好一些的解决方法是使用sscanf
函数,但它也并不完美。要将字符串转换为整数,应该使用strto...
函数组中的函数。在这种情况下,应该使用strtol
函数。实际上,如果sscanf
尝试转换超出其类型范围的数字(例如,sscanf("999999999999999999999", "%d", &n)
),它会产生未定义的行为。Thompson指出,atoi
没有提供有意义的成功/失败反馈,并且在溢出时具有未定义的行为。而sscanf
在溢出时提供了某种程度的成功/失败反馈(即返回值),但仍然具有溢出时的未定义行为。因此,唯一可行的解决方案是使用strtol
函数。作者也表示同意,并强调了sscanf
可能致命的问题。尽管他承认有时会使用atoi
,但通常只用于不需要源代码存活超过10分钟的程序。
问题的原因是需要将字符串转换为整数数组存储。解决方法是使用C语言中的函数来进行转换。
在C语言中,可以使用函数`strtol`来将字符串转换为长整型(long long)。这是一种更好的方法。另外,还可以使用函数`strtonum`来进行转换,但需要注意它不是可移植的。以下是使用`strtonum`函数的示例代码:
long long strtonum(const char *nptr, long long minval, long long maxval, const char **errstr);
另外,C99标准中还提供了函数`strtoumax`和`strtoimax`,它们可以用来将字符串转换为无符号整型和有符号整型。以下是使用`strtoumax`函数的示例代码:
uintmax_t num = strtoumax(s, NULL, 10); if (num == UINTMAX_MAX && errno == ERANGE) /* 无法转换 */
需要注意的是,应避免使用`atoi`函数,因为它的错误处理方式可能不同。如果无法表示转换的值,它的行为是不确定的。
关于`strtonum`函数的引用问题,如果系统支持该函数,应在`stdlib.h`头文件中声明。但是,也可以使用标准的`strtoumax`函数作为替代。
某些情况下无法找到`strtonum`函数的引用,可能是因为使用的是libc 2.15版本。可能是因为使用了`-ANSI`标志的原因,但即使去掉该标志也无法找到。
某些情况下这个回答似乎并没有比提问者的第一个代码更简洁。简洁固然好,但不能以牺牲正确性为代价。
某些情况下`atoi`函数的问题在于它不安全。如果输入无效,`atoi`函数将无法正常工作。而`strtol`函数在无法表示值时有定义的行为。
至于为什么说`strtol`函数更好,没有给出具体原因。
如何将字符串转换为存储在整数数组中的问题的出现是因为需要将一个字符串转换为整数数组。下面是解决这个问题的方法:
本解决方案基于C89标准的可靠的`strtol`函数。它具有以下特点:
- 没有未定义的行为(与`atoi`系列函数可能存在的情况相比)
- 比`strtol`更严格的整数定义(例如,没有前导空格和尾随非数字字符)
- 对错误情况进行分类(例如,向用户提供有用的错误消息)
- 一个“测试套件”
下面是解决方案的代码:
#include#include #include #include #include #include typedef enum { STR2INT_SUCCESS, STR2INT_OVERFLOW, STR2INT_UNDERFLOW, STR2INT_INCONVERTIBLE } str2int_errno; str2int_errno str2int(int *out, char *s, int base) { char *end; if (s[0] == '\0' || isspace(s[0])) return STR2INT_INCONVERTIBLE; errno = 0; long l = strtol(s, &end, base); if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW; if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN)) return STR2INT_UNDERFLOW; if (*end != '\0') return STR2INT_INCONVERTIBLE; *out = l; return STR2INT_SUCCESS; } int main(void) { int i; char s[256]; assert(str2int(&i, "11", 10) == STR2INT_SUCCESS); assert(i == 11); assert(str2int(&i, "-11", 10) == STR2INT_SUCCESS); assert(i == -11); assert(str2int(&i, "11", 16) == STR2INT_SUCCESS); assert(i == 17); assert(str2int(&i, "0", 10) == STR2INT_SUCCESS); assert(i == 0); sprintf(s, "%d", INT_MAX); assert(str2int(&i, s, 10) == STR2INT_SUCCESS); assert(i == INT_MAX); sprintf(s, "%d", INT_MIN); assert(str2int(&i, s, 10) == STR2INT_SUCCESS); assert(i == INT_MIN); assert(str2int(&i, " 1", 10) == STR2INT_INCONVERTIBLE); assert(str2int(&i, "1 ", 10) == STR2INT_INCONVERTIBLE); assert(str2int(&i, "a10", 10) == STR2INT_INCONVERTIBLE); assert(str2int(&i, "10a", 10) == STR2INT_INCONVERTIBLE); if (INT_MAX < LONG_MAX) { sprintf(s, "%ld", (long int)INT_MAX + 1L); assert(str2int(&i, s, 10) == STR2INT_OVERFLOW); } if (LONG_MIN < INT_MIN) { sprintf(s, "%ld", (long int)INT_MIN - 1L); assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW); } sprintf(s, "%ld0", LONG_MAX); assert(str2int(&i, s, 10) == STR2INT_OVERFLOW); sprintf(s, "%ld0", LONG_MIN); assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW); return EXIT_SUCCESS; }
这个解决方案使用了`strtol`函数来实现字符串到整数的转换,并进行了错误检查和分类。它提供了一个`str2int`函数,接受一个字符串和一个基数作为输入,并将转换后的整数存储在指定的输出参数中。返回值是一个`str2int_errno`枚举类型,表示转换的结果或错误类型。在`main`函数中进行了一些简单的测试来验证解决方案的正确性。
这个解决方案提供了一个可靠且健壮的方法来将字符串转换为存储在整数数组中的整数。它避免了一些常见的问题,如未定义的行为、不正确的转换和错误类型的分类。通过使用`strtol`函数和适当的错误处理,我们可以确保转换的结果是准确的,并且能够提供有用的错误信息。