C atof 구현

Aug 21 2020

저는 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);
}

내가 만든 실제 atof에 대한 유일한 조정은 이중 문자 포인터를 인수로 사용하고 오류가 발생하면 -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시스템 호출에만 설정 하는 것입니다. 이 전통이 요즘 점점 더 위반되고 있다는 것을 알고 있지만 여전히 그렇습니다. 다른 오류보고 수단이있는 경우이를 고수하십시오.

  • inout 매개 변수 ( str귀하의 경우)를 사용하는 것은 권장되지 않습니다. 호출자 측과 수신자 측 모두에서 코드를 불필요하게 복잡하게 만듭니다. 피 호출자는 추가 간접 지정을 너무 많이 사용하고 괄호 안에 괄호를 넣는 것에 대해 걱정해야 (**str)++합니다. 차례로 호출자는 구문 분석이 시작된 위치를 추적하지 못합니다 (예 : 잘못된 번호를 기록해야 함). 어떻게 strtof처리 하는지보세요 :

      float strtof(const char *restrict nptr, char **restrict endptr);
    

    여기는 nptrin-only, endptrout-only입니다.

  • 소수점 이하 한 자리 만 처리하여 함수의 유용성을 제한하기로 결정한 것에 놀랐습니다. 그것들을 모두 처리하는 것은 큰 노력이 아니며 이점이 훨씬 큽니다.

  • 반환 값을 괄호로 묶을 필요가 없습니다. return함수가 아니라 연산자입니다.