首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >安全简单的strtol()

安全简单的strtol()
EN

Code Review用户
提问于 2019-08-28 01:30:03
回答 1查看 2.6K关注 0票数 6

直到今天,在我需要这样的东西的少数情况下,我只使用了简单的程序,而我并不关心安全性,所以我使用了简单的atoi()

然而,今天我需要做一些更严肃的程序,我研究了从字符串到数字的许多不同的形式:atoi vs atol vs strtol vs strtoul vs sscanf

我对这些都不满意。strtol() (及其家族)是最安全的标准之一,也是一个非常快速的标准,但它的使用非常困难,所以我决定为它编写一个安全而简单的接口。strtoi() (libbsd)比strtol()更容易使用,但仍然有点复杂。我决定使用固定宽度的整数,就像我在所有代码中所做的那样。我还为strtof()和公司做了一个界面。

必要条件:

  • libbsd (如果libbsd不可用,下面的代码可以用strtol()而不是strtoi()编写,但它更复杂,并且errnostrtoi()没有的问题)。
  • GNU C11 (实际上并不需要,但我使用它来增加安全性/优化)。

有符号整数:

strtoi_s.h

代码语言:javascript
运行
复制
#pragma once    /* libalx/base/stdlib/strto/strtoi_s.h */


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


__attribute__((nonnull, warn_unused_result))
inline
int     strtoi8_s   (int8_t *restrict num, const char *restrict str,
                     int base);
__attribute__((nonnull, warn_unused_result))
inline
int     strtoi16_s  (int16_t *restrict num, const char *restrict str,
                     int base);
__attribute__((nonnull, warn_unused_result))
inline
int     strtoi32_s  (int32_t *restrict num, const char *restrict str,
                     int base);
__attribute__((nonnull, warn_unused_result))
inline
int     strtoi64_s  (int64_t *restrict num, const char *restrict str,
                     int base);


inline
int     strtoi8_s   (int8_t *restrict num, const char *restrict str,
                     int base)
{
        int     rstatus;

        *num    = strtoi(str, NULL, base, INT8_MIN, INT8_MAX, &rstatus);

        switch (rstatus) {
        case 0:
                return  0;
        case ENOTSUP:
                return  rstatus;
        case ECANCELED:
        case EINVAL:
        case ERANGE:
        default:
                return  -rstatus;
        }
}

inline
int     strtoi16_s  (int16_t *restrict num, const char *restrict str,
                     int base)
{
        int     rstatus;

        *num    = strtoi(str, NULL, base, INT16_MIN, INT16_MAX, &rstatus);

        switch (rstatus) {
        case 0:
                return  0;
        case ENOTSUP:
                return  rstatus;
        case ECANCELED:
        case EINVAL:
        case ERANGE:
        default:
                return  -rstatus;
        }
}

inline
int     strtoi32_s  (int32_t *restrict num, const char *restrict str,
                     int base)
{
        int     rstatus;

        *num    = strtoi(str, NULL, base, INT32_MIN, INT32_MAX, &rstatus);

        switch (rstatus) {
        case 0:
                return  0;
        case ENOTSUP:
                return  rstatus;
        case ECANCELED:
        case EINVAL:
        case ERANGE:
        default:
                return  -rstatus;
        }
}

inline
int     strtoi64_s  (int64_t *restrict num, const char *restrict str,
                     int base)
{
        int     rstatus;

        *num    = strtoi(str, NULL, base, INT64_MIN, INT64_MAX, &rstatus);

        switch (rstatus) {
        case 0:
                return  0;
        case ENOTSUP:
                return  rstatus;
        case ECANCELED:
        case EINVAL:
        case ERANGE:
        default:
                return  -rstatus;
        }
}

无符号整数:

它与前一个基本相同,所以我只发布一个函数

strtou_s.h

代码语言:javascript
运行
复制
inline
int     strtou8_s   (uint8_t *restrict num, const char *restrict str,
                     int base)
{
        int     rstatus;

        *num    = strtou(str, NULL, base, 0, UINT8_MAX, &rstatus);

        switch (rstatus) {
        case 0:
                return  0;
        case ENOTSUP:
                return  rstatus;
        case ECANCELED:
        case EINVAL:
        case ERANGE:
        default:
                return  -rstatus;
        }
}

浮点:

strtof_s.h

代码语言:javascript
运行
复制
#pragma once    /* libalx/base/stdlib/strto/strtof_s.h */


#include <errno.h>
#include <stdlib.h>


/*
 * `errno` needs to be cleared before calling these functions.  If not, false
 * negatives could happen (the function succeds, but it reports error).
 */
__attribute__((nonnull, warn_unused_result))
inline
int     strtod_s    (double *restrict num, const char *restrict str);
__attribute__((nonnull, warn_unused_result))
inline
int     strtof_s    (float *restrict num, const char *restrict str);
__attribute__((nonnull, warn_unused_result))
inline
int     strtold_s   (long double *restrict num, const char *restrict str);


inline
int     strtod_s    (double *restrict num, const char *restrict str)
{
        char    *endptr;

        *num    = strtod(str, &endptr);

        if (*endptr != '\0')
                return  ENOTSUP;
        if (errno == ERANGE)
                return  ERANGE;
        if (str == endptr)
                return  -ECANCELED;

        return  0;
}

inline
int     strtof_s    (float *restrict num, const char *restrict str)
{
        char    *endptr;

        *num    = strtof(str, &endptr);

        if (*endptr != '\0')
                return  ENOTSUP;
        if (errno == ERANGE)
                return  ERANGE;
        if (str == endptr)
                return  -ECANCELED;

        return  0;
}

inline
int     strtold_s   (long double *restrict num, const char *restrict str)
{
        char    *endptr;

        *num    = strtold(str, &endptr);

        if (*endptr != '\0')
                return  ENOTSUP;
        if (errno == ERANGE)
                return  ERANGE;
        if (str == endptr)
                return  -ECANCELED;

        return  0;
}

函数有两个指针:第一个指向必须存储数字的变量;第二个指向要读取的字符串。整数函数还需要基函数,它遵循与strtol()中相同的规则。

返回值只是一个错误代码:

0和往常一样好,

> 0是指带有某些错误的有效转换(浮点中的部分转换,0或inf,.)。

< 0意味着无效的转换,或者根本没有转换。

示例:

代码语言:javascript
运行
复制
char    buf[BUFSIZ];
int64_t num;

if (!fgets(buf, ARRAY_SIZE(buf), stdin))
        goto err;
if (strtoi64_s(&num, buf, 0))
        goto err;

/* num is safe to be used now*/

你认为界面可以用任何方式改进吗?

EN

回答 1

Code Review用户

发布于 2019-08-28 18:15:29

  • strtol和家庭的主要优点是他们计算(免费!)转换结束的点。这是一个非常有价值的信息,因为通常在提取出要继续解析的数字之后。你的包装纸扔掉了。
  • 干的。积分类型的开关(计算result)是相同的。把它分解成一个函数。
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/226969

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档