C Thực hiện atof
Tôi là người mới bắt đầu tại C. Tôi hiện đang triển khai xây dựng một raytracer, tuy nhiên tôi vẫn đang học cách viết chương trình một cách hiệu quả.
Chuyển nhượng
Hướng dẫn
Chương trình lấy một tệp mô tả cảnh làm đối số để tạo các đối tượng. Một số paramaters là phao. Ví dụ về tệp.
Tôi đang phân tích tệp. Vì tôi bị hạn chế về số dòng được phép cho mỗi hàm và tôi hiện đang tìm hiểu cách hoạt động của con trỏ kép, tôi đang sử dụng con trỏ ký tự kép. Ví dụ về một chức năng như vậy bằng cách sử dụng 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);
}
Mã hiện tại để xem lại:
#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);
}
Các chỉnh sửa duy nhất đối với thực tế mà tôi đã thực hiện là có một con trỏ char kép làm đối số và trả về -1 trong trường hợp có lỗi.
Mọi đầu vào được đánh giá cao.
Trả lời
Tính di động
Không có gì đảm bảo rằng mã này sẽ sử dụng ASCII nên tốt hơn là sử dụng '0'chứ không phải 48là một con số ma thuật. Sử dụng '0'làm cho nó dễ đọc hơn và dễ hiểu hơn.
lc_atof Không xử lý chấm dứt chuỗi hoặc kết thúc dòng một cách chính xác
Mã này không xử lý một chuỗi kết thúc NULL hoặc một ký tự cuối dòng. Hàm isspace()trả về giá truetrị cuối dòng để mã sẽ đi qua nó.
while (isspace(**str))
(*str)++;
if (**str == '+' || **str == '-')
{
if (**str == '-')
sign = -1.0;
(*str)++;
}
if (!isdigit(**str))
{
errno = EIO;
return (-1);
}
Phức tạp
Tôi tin rằng bạn không yêu cầu xem xét điều này, nhưng độ phức tạp của mỗi ifcâu lệnh trong hàm gọi ví dụ là quá nhiều và đã khiến tôi mắc lỗi trong bài đánh giá của mình trước đây:
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);
}
Tôi sẽ viết lại mã dưới dạng:
#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);
}
cho thấy một cách chân thực sự phức tạp của hàm.
Lựa chọn
EIObáo cáo lỗi là rất khó hiểu.lc_atofkhông thực hiện bất kỳ đầu vào hoặc đầu ra; tại sao nó phải báo lỗi IO? Nếu kiểu trả về không thể đại diện cho kết quả (ví dụd_nbr > FLT_MAX), một lựa chọn hợp lý làERANGEhoặcEOVERFLOW. Nếu quá trình chuyển đổi không thể hoàn tất do đối số không đúng định dạng (ví dụ!isdigit(**str)), lựa chọn hợp lý có lẽ sẽ làEINVAL.Điều đó nói rằng, tôi không xác nhận cài đặt
errnotrong chức năng thư viện. Một truyền thống lâu đời là chỉ đặterrnotrong các lệnh gọi hệ thống. Tôi biết rằng truyền thống này ngày càng bị vi phạm nhiều hơn trong những ngày này, nhưng vẫn còn. Nếu bạn có các phương tiện thông báo lỗi khác, hãy sử dụng chúng.Sử dụng tham số inout (
strtrong trường hợp của bạn) là không nên. Nó làm phức tạp mã một cách không cần thiết, cả ở phía người gọi và phía người gọi. Callee bị buộc phải sử dụng thêm hướng dẫn quá nhiều lần và phải lo lắng về việc đặt dấu ngoặc đơn(**str)++. Đổi lại, người gọi mất theo dõi nơi có thể phân tích cú pháp bắt đầu (giả sử, nó cần ghi lại số không đúng định dạng). Hãy xem cáchstrtofxử lý điều này:float strtof(const char *restrict nptr, char **restrict endptr);Đây
nptrlà chỉ trong vàendptrngoài.Tôi rất ngạc nhiên khi bạn quyết định giới hạn tiện ích của hàm bằng cách chỉ xử lý một chữ số sau dấu chấm thập phân. Việc xử lý tất cả chúng không phải là một nỗ lực lớn và lợi ích thu được còn lớn hơn nhiều.
Không cần đặt giá trị trả về trong ngoặc đơn.
returnlà một toán tử, không phải là một hàm.