首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C. atof的实施

C. atof的实施
EN

Code Review用户
提问于 2020-08-20 17:46:07
回答 2查看 2.5K关注 0票数 9

我是C的初学者,我目前正在实现atof来构建一个射线追踪器,但是我仍然在学习如何有效地编写程序。

分配

指令

该程序以场景描述文件作为参数来生成对象。其中一些参数是浮动的。该文件的示例。

我在分析这个文件。由于限制了每个函数允许的行数,而且我目前正在学习双指针是如何工作的,所以我使用了一个双字符指针。使用lc_atof的这样一个函数的例子。

代码语言:javascript
复制
int    a_parsing(char *str, t_pars *data)
{
    if (*(str++) == 'A')
    {
        if (((data->a_ratio = lc_atof(&str)) >= 0.0) && data->a_ratio <= 1.0 && errno == 0)
        // 
            if (((data->a_R = lc_atoi(&str)) >= 0) && data->a_R <= 255 && errno == 0)
                if (*(str++) = ',' && ((data->a_G = lc_atoi(&str)) >= 0) && data->a_G <= 255 && errno == 0)
                    if (*(str++) = ',' && ((data->a_B = lc_atoi(&str)) >= 0) && data->a_B <= 255 && errno == 0)
                        return (skip_space(&str));
    }
    return (0);
}

当前要检查的代码:

代码语言:javascript
复制
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <float.h>

static float    conversion(char **str)
{
    double    d_nbr;
    double    power;

    d_nbr = 0.0;
    power = 10.0;
    while (isdigit(**str))
    {
        d_nbr = d_nbr * 10.0 + (**str - 48);
        if (d_nbr > FLT_MAX)
        {
            errno = EIO;
            return (-1);
        }
        (*str)++;
    }
    if (**str == '.')
    {
      (*str)++;
      if (isdigit(**str))
      {
        d_nbr = d_nbr * 10.0 + (**str - 48);
        if (d_nbr > FLT_MAX)
        {
            errno = EIO;
            return (-1);
        }
        (*str)++;
        return ((float)(d_nbr / power));
      }
    }
    errno = EIO;
    return (-1);
}

float            lc_atof(char **str)
{
    float    n;
    int        sign;

    n = 0.0;
    sign = 1.0;
    if (!str || !*str)
    {
        errno = EIO;
        return (-1);
    }
    while (isspace(**str))
        (*str)++;
    if (**str == '+' || **str == '-')
    {
        if (**str == '-')
            sign = -1.0;    
        (*str)++;
    }
    if (!isdigit(**str))
    {
        errno = EIO;
        return (-1);
    }
    if ((n = conversion(str)) == 0 && errno != 0)
       return (-1);
    return (sign * n);
}

我所做的唯一的调整就是使用一个双字符指针作为参数,并在发生错误时返回-1。

每一项投入都非常感谢。

EN

回答 2

Code Review用户

回答已采纳

发布于 2020-08-20 18:38:49

便携性

不能保证这段代码将使用ASCII,所以最好使用'0'而不是48,这是一个神奇的数字。使用'0'使其更易读和更容易理解。

lc_atof没有正确处理字符串终止或行尾

此代码不处理以空结尾的字符串或行结束字符。函数isspace()返回行尾的true,因此代码将直接通过它。

代码语言:javascript
复制
    while (isspace(**str))
        (*str)++;
    if (**str == '+' || **str == '-')
    {
        if (**str == '-')
            sign = -1.0;
        (*str)++;
    }
    if (!isdigit(**str))
    {
        errno = EIO;
        return (-1);
    }

复杂性

我认为您并没有要求对此进行检查,但是示例call函数中每个if语句的复杂性太高,导致我在前面的评审中出错:

代码语言:javascript
复制
int a_parsing(char* str, t_pars* data)
{
    if (*(str++) == 'A')
    {
        if (((data->a_ratio = lc_atof(&str)) >= 0.0) && data->a_ratio <= 1.0 && errno == 0)
            // 
            if (((data->a_R = lc_atoi(&str)) >= 0) && data->a_R <= 255 && errno == 0)
                if (*(str++) = ',' && ((data->a_G = lc_atoi(&str)) >= 0) && data->a_G <= 255 && errno == 0)
                    if (*(str++) = ',' && ((data->a_B = lc_atoi(&str)) >= 0) && data->a_B <= 255 && errno == 0)
                        return (skip_space(&str));
    }
    return (0);
}

我会将代码重写为:

代码语言:javascript
复制
#define MAX_COLOR   0xFF
int a_parsing_prime(char* str, t_pars* data)
{
    if (*(str++) == 'A')
    {
        data->a_ratio = lc_atof(&str);
        if (!errno && data->a_R <= MAX_COLOR)
        {
            if (*(str++) = ',')
            {
                data->a_G = lc_atoi(&str);
                if (!errno && data->a_G <= MAX_COLOR)
                {
                    if (*(str++) = ',')
                    {
                        data->a_B = lc_atoi(&str);
                        if (!errno && data->a_B <= MAX_COLOR)
                        {
                            return (skip_space(&str));
                        }
                    }
                }
            }
        }
    }
    return (0);
}

它真实地显示了函数的复杂性。

票数 10
EN

Code Review用户

发布于 2020-08-20 23:19:38

  • 选择EIO作为错误报告是非常可疑的。lc_atof不执行任何输入或输出;为什么要报告IO错误?如果返回类型不能表示结果(例如d_nbr > FLT_MAX),则逻辑选择是ERANGEEOVERFLOW。如果转换由于格式错误的参数(例如!isdigit(**str))而无法完成,则逻辑选择可能是EINVAL。尽管如此,我不赞成在库函数中设置errno。一个长期的传统是只在系统调用中设置errno。我知道现在这个传统被越来越多地违反了,但是仍然。如果你有其他的错误报告方法,坚持他们。
  • 使用inout参数(在您的情况下是str)是不可取的。它不必要地使代码复杂化,无论是在调用方还是被调用方。被叫者被迫使用额外的间接次数太多,并担心括号大小的(**str)++。反过来,调用方将失去可解析的开始位置(例如,它需要记录格式错误的数字)。看看strtof是如何处理这个问题的: float (const*限制nptr,char **限制endptr);在这里,nptr是只入的,而endptr是只有out的。
  • 令我惊讶的是,您决定通过只处理小数点之后的一位数来限制函数的效用。要解决所有这些问题并不是一项很大的努力,而且好处要大得多。
  • 不需要用括号表示返回值。return是一个运算符,不是一个函数。
票数 10
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

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

复制
相关文章

相似问题

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