【问题标题】:Safe and portable way to convert a char* to uint16_t将 char* 转换为 uint16_t 的安全且可移植的方法
【发布时间】:2013-11-29 22:48:36
【问题描述】:

正如标题中提到的,我正在寻找一种将 char*(来自 argv)转换为 uint16_t 的方法。命令行参数是端口号,所以不能>到65535,也不能为负数。

目前,我这样做了(使用 -std=gnu99 编译):

#include <stdbool.h>
#include <errno.h>
#include <stdint.h>
#include <inttypes.h>

/*
 * Converts a string to an unsigned int and stores the result in "res".
 */
bool str_to_uint(const char* str, unsigned long int* res) {
    if (str[0] == '-')
        return false;
    char* first_wrong_character;
    uintmax_t result = strtoumax(str, &first_wrong_character, 10);
    if ((result == UINTMAX_MAX) && (errno == ERANGE))
        return false; // Overflow)
    if ((*str != '\0') && (*first_wrong_character != '\0'))
        return false; // Not everything has been converted
    if ((result == 0) && (str == first_wrong_character))
        return false; // Nothing to convert
    *res = result;
    return true;
}

/*
 * Converts a string to an uint16_t and stores the result in "res".
 */
bool str_to_uint16(const char* str, uint16_t* res) {
    unsigned long uint;
    if (!str_to_uint(str, &uint))
        return false;
    if (uint > UINT16_MAX)
        return false;
    *res = (uint16_t)uint;
    return true;
}

我不确定这是最好的方法,所以如果你能告诉我什么是好方法?

【问题讨论】:

    标签: c char integer


    【解决方案1】:

    您可以使用strtol(3),它能够在整数溢出的情况下返回错误(ERANGE),并简单地检查解析的整数与uint16_t容量相比是否太大:

    #include <stdint.h> /* fixed-width integer types */
    #include <stdlib.h> /* strtol */
    #include <stdbool.h>
    #include <errno.h>
    
    static bool
    str_to_uint16(const char *str, uint16_t *res)
    {
      long int val = strtol(str, NULL, 10);
      if (errno == ERANGE || val > UINT16_MAX || val < 0)
        return false;
      *res = (uint16_t) val;
      return true;
    }
    

    编辑

    由于问题涉及 C99,如果我包含更好的错误管理(感谢 @nwellnhof 和 @chux),我会说下面的版本应该是正确的候选者:

    #include <inttypes.h> /* strtoimax */
    
    static bool
    str_to_uint16(const char *str, uint16_t *res)
    {
      char *end;
      errno = 0;
      intmax_t val = strtoimax(str, &end, 10);
      if (errno == ERANGE || val < 0 || val > UINT16_MAX || end == str || *end != '\0')
        return false;
      *res = (uint16_t) val;
      return true;
    }
    

    它成功了:

    • 1981
    • 65535 (UINT16_MAX)

    它返回一个转换错误(如预期):

    • 65536 (UINT16_MAX+1)
    • a1981
    • 1981a
    • abcd
    • 9223372036854775808INTMAX_MAX+1:在这种情况下出现ERANGE
    • -9223372036854775809INTMAX_MIN-1:在这种情况下出现ERANGE

    【讨论】:

    • 我用溢出号 (65536) 尝试了这个,我的变量中得到了 0 而不是错误。此外,PRIu16 是“%u”而不是“%hu”(h = 短)。顺便说一句,我不知道为什么 PRIu16 不是 %hu。
    • 我已经更新了我的答案:sscanf 确实不是一个好主意,因为它无法防止整数溢出。 strtol 因此是合适的候选人(见上文)。
    【解决方案2】:

    没有必要使用strtoumax。我会选择更便携的strtol。错误处理也可以简化为:

    bool str_to_uint16(const char *str, uint16_t *res) {
        char *end;
        errno = 0;
        long val = strtol(str, &end, 10);
        if (errno || end == str || *end != '\0' || val < 0 || val >= 0x10000) {
            return false;
        }
        *res = (uint16_t)val;
        return true;
    }
    

    【讨论】:

    • 你的错误管理(与end 检查相比)比我的要好:) 但我猜你的意思是0x10000 而不是0x1000 的上限,对吧?
    • 在这种情况下 strtoumax() 的目标是什么?为什么不在您的示例中使用 strtoul ? 0x1000 是什么?
    • 对,我的意思是0x10000。固定。
    • val &gt; UINT16_MAX 会更好。
    • 不知道errno是怎么回事
    猜你喜欢
    • 1970-01-01
    • 2021-11-16
    • 2020-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多