Catofの実装

Aug 21 2020

私は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を返すことです。

すべての入力に感謝します。

回答

10 pacmaninbw Aug 21 2020 at 01:38

移植性

このコードが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);
}

これは本当に関数の複雑さを示しています。

10 vnp Aug 21 2020 at 06:19
  • 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は演算子であり、関数ではありません。