C การใช้งาน atof
ฉันเป็นมือใหม่ที่ C. ฉันกำลังใช้ atof เพื่อสร้าง raytracer แต่ฉันยังคงเรียนรู้วิธีการเขียนโปรแกรมอย่างมีประสิทธิภาพ
การมอบหมาย
คำแนะนำ
โปรแกรมใช้ไฟล์คำอธิบายฉากเป็นอาร์กิวเมนต์เพื่อสร้างวัตถุ บางส่วนของพารานี้ลอยได้ ตัวอย่างไฟล์
ฉันกำลังแยกวิเคราะห์ไฟล์ เนื่องจากฉันถูก จำกัด ในแง่ของบรรทัดที่อนุญาตต่อฟังก์ชันและฉันกำลังเรียนรู้ว่าพอยน์เตอร์คู่ทำงานอย่างไรฉันจึงใช้ตัวชี้ถ่านคู่ 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 ในกรณีที่เกิดข้อผิดพลาด
ทุกข้อมูลชื่นชมมาก
คำตอบ
การพกพา
ไม่มีการรับประกันว่ารหัสนี้จะใช้ 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) เป็นทางเลือกที่ตรรกะหรือERANGEEOVERFLOWหากการแปลงไม่สามารถดำเนินการให้เสร็จสิ้นได้เนื่องจากอาร์กิวเมนต์ผิดรูปแบบ (เช่น!isdigit(**str)) ตัวเลือกเชิงตรรกะอาจเป็นEINVALได้ที่กล่าวว่าฉันไม่รับรองการตั้งค่า
errnoในฟังก์ชันไลบรารี ประเพณีที่สืบทอดกันมายาวนานคือการตั้งค่าerrnoในการโทรระบบเท่านั้น ฉันรู้ว่าประเพณีนี้ถูกละเมิดมากขึ้นเรื่อย ๆ ในทุกวันนี้ แต่ก็ยังคง หากคุณมีวิธีอื่นในการรายงานข้อผิดพลาดให้ปฏิบัติตามstrไม่แนะนำให้ใช้พารามิเตอร์ inout ( ในกรณีของคุณ) ทำให้โค้ดซับซ้อนโดยไม่จำเป็นทั้งฝั่งผู้โทรและฝั่งผู้โทร ผู้ถูกเรียกถูกบังคับให้ต้องใช้ตรงพิเศษหลายครั้งเกินไปและกังวลเกี่ยวกับ(**str)++parenthesizing ในทางกลับกันผู้โทรจะสูญเสียการติดตามว่าการแยกวิเคราะห์เริ่มต้นที่ใด (เช่นต้องบันทึกหมายเลขที่ผิดรูปแบบ) ดูวิธีstrtofจัดการสิ่งนี้:float strtof(const char *restrict nptr, char **restrict endptr);นี่
nptrคือเฉพาะในและendptrนอกเท่านั้นฉันแปลกใจที่คุณตัดสินใจ จำกัด ยูทิลิตี้ของฟังก์ชันนี้โดยจัดการเพียงหนึ่งหลักหลังจุดทศนิยม ไม่ใช่ความพยายามครั้งใหญ่ในการจัดการทั้งหมดและผลประโยชน์นั้นยิ่งใหญ่กว่ามาก
ไม่จำเป็นต้องวงเล็บค่าส่งคืน
returnเป็นตัวดำเนินการไม่ใช่ฟังก์ชัน