将char *转换为uint16_t的安全且可移植的方法

时间:2013-11-16 14:52:35

标签: c char integer

正如标题中所提到的,我正在寻找一种方法将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;
}

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

2 个答案:

答案 0 :(得分:5)

您可以使用能够在整数溢出(ERANGE)的情况下返回错误的strtol(3),并简单地检查解析的整数是否与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
  • 65535UINT16_MAX

它返回转换错误(按预期方式):

  • 65536UINT16_MAX+1
  • a1981
  • 1981a
  • abcd
  • 9223372036854775808INTMAX_MAX+1:在这种情况下发生ERANGE
  • -9223372036854775809INTMAX_MIN-1:在这种情况下发生ERANGE

答案 1 :(得分:5)

无需使用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;
}