Catofの実装
私はCの初心者です。現在、レイトレーサーを構築するためにatofを実装していますが、プログラムを効率的に作成する方法をまだ学んでいます。
割り当て
指示
プログラムは、オブジェクトを生成するための引数としてシーン記述ファイルを取ります。これらのパラメータのいくつかはフロートです。ファイルの例。

ファイルを解析しています。関数ごとに許可される行数に制限があり、現在、doubleポインターがどのように機能するかを学習しているため、doublecharポインターを使用しています。を使用したそのような関数の例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);
}
私が行った実際のatofに対する唯一の調整は、引数としてdouble charポインターを持ち、エラーの場合に-1を返すことです。
すべての入力に感謝します。
回答
移植性
このコードがASCIIを使用するという保証はないので、マジックナンバーのようなもの'0'
よりも使用する方がよいでしょう48
。を使用'0'
すると、読みやすく、理解しやすくなります。
lc_atof
文字列の終了または行末を正しく処理しません
このコードは、NULLで終了する文字列または行末文字を処理しません。関数isspace()
はtrue
行末を返すので、コードは行末を通り過ぎます。
while (isspace(**str))
(*str)++;
if (**str == '+' || **str == '-')
{
if (**str == '-')
sign = -1.0;
(*str)++;
}
if (!isdigit(**str))
{
errno = EIO;
return (-1);
}
複雑
これをレビューするように依頼しなかったと思いますが、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);
}
これは本当に関数の複雑さを示しています。
EIO
エラー報告の選択は非常に疑わしいです。lc_atof
入力または出力を行いません。なぜIOエラーを報告する必要があるのですか?戻り値の型が結果を表すことができない場合(たとえばd_nbr > FLT_MAX
)、論理的な選択はERANGE
またはEOVERFLOW
です。不正な形式の引数(例!isdigit(**str)
)が原因で変換を完了できない場合、論理的な選択はおそらくですEINVAL
。とはいえ
errno
、ライブラリ関数の設定は推奨していません。長年の伝統はerrno
、システムコールのみを設定することです。私はこの伝統が最近ますます違反されていることを知っていますが、それでもなおです。エラー報告の他の手段がある場合は、それらに固執してください。(
str
あなたの場合)inoutパラメータを使用することはお勧めできません。呼び出し側と呼び出し側の両方で、コードが不必要に複雑になります。呼び出し先は、余分な間接参照を何度も使用することを余儀なくされ、括弧で囲まれることを心配します(**str)++
。次に、呼び出し元は、解析可能ファイルがどこから始まったかを追跡できなくなります(たとえば、不正な形式の番号をログに記録する必要があります)。strtof
これをどのように処理するかを見てください。float strtof(const char *restrict nptr, char **restrict endptr);
ここ
nptr
でのみで、endptr
アウトのみです。10進数のドットの後の1桁だけを処理することによって、関数の有用性を制限することにしたことに驚いています。それらすべてを処理することは大きな努力ではなく、利点ははるかに大きいです。
戻り値を括弧で囲む必要はありません。
return
は演算子であり、関数ではありません。