我是C的初学者,我目前正在实现atof来构建一个射线追踪器,但是我仍然在学习如何有效地编写程序。
该程序以场景描述文件作为参数来生成对象。其中一些参数是浮动的。该文件的示例。

我在分析这个文件。由于限制了每个函数允许的行数,而且我目前正在学习双指针是如何工作的,所以我使用了一个双字符指针。使用lc_atof的这样一个函数的例子。
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);
}#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。
每一项投入都非常感谢。
发布于 2020-08-20 18:38:49
不能保证这段代码将使用ASCII,所以最好使用'0'而不是48,这是一个神奇的数字。使用'0'使其更易读和更容易理解。
lc_atof没有正确处理字符串终止或行尾此代码不处理以空结尾的字符串或行结束字符。函数isspace()返回行尾的true,因此代码将直接通过它。
while (isspace(**str))
(*str)++;
if (**str == '+' || **str == '-')
{
if (**str == '-')
sign = -1.0;
(*str)++;
}
if (!isdigit(**str))
{
errno = EIO;
return (-1);
}我认为您并没有要求对此进行检查,但是示例call函数中每个if语句的复杂性太高,导致我在前面的评审中出错:
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);
}我会将代码重写为:
#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);
}它真实地显示了函数的复杂性。
发布于 2020-08-20 23:19:38
EIO作为错误报告是非常可疑的。lc_atof不执行任何输入或输出;为什么要报告IO错误?如果返回类型不能表示结果(例如d_nbr > FLT_MAX),则逻辑选择是ERANGE或EOVERFLOW。如果转换由于格式错误的参数(例如!isdigit(**str))而无法完成,则逻辑选择可能是EINVAL。尽管如此,我不赞成在库函数中设置errno。一个长期的传统是只在系统调用中设置errno。我知道现在这个传统被越来越多地违反了,但是仍然。如果你有其他的错误报告方法,坚持他们。str)是不可取的。它不必要地使代码复杂化,无论是在调用方还是被调用方。被叫者被迫使用额外的间接次数太多,并担心括号大小的(**str)++。反过来,调用方将失去可解析的开始位置(例如,它需要记录格式错误的数字)。看看strtof是如何处理这个问题的: float (const*限制nptr,char **限制endptr);在这里,nptr是只入的,而endptr是只有out的。return是一个运算符,不是一个函数。https://codereview.stackexchange.com/questions/248205
复制相似问题