C Penerapan atof
Saya seorang pemula di C. Saat ini saya menerapkan atof untuk membuat raytracer, namun saya masih belajar cara menulis program secara efisien.
Penugasan
Instruksi
Program mengambil file deskripsi adegan sebagai argumen untuk menghasilkan objek. Beberapa dari parameter ini mengapung. Contoh file.

Saya sedang mem-parsing file. Karena saya dibatasi dalam hal garis yang diizinkan per fungsi dan saya sedang mempelajari cara kerja penunjuk ganda, saya menggunakan penunjuk karakter ganda. Contoh salah satu fungsi tersebut menggunakan 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);
}
Kode saat ini untuk ditinjau:
#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);
}
Satu-satunya perubahan pada atof aktual yang saya buat adalah memiliki penunjuk karakter ganda sebagai argumen dan mengembalikan -1 jika terjadi kesalahan.
Setiap masukan sangat dihargai.
Jawaban
Portabilitas
Tidak ada jaminan bahwa kode ini akan menggunakan ASCII jadi akan lebih baik digunakan '0'
daripada 48
yang merupakan angka ajaib. Menggunakan '0'
membuatnya lebih mudah dibaca dan dipahami.
lc_atof
Tidak Menangani Penghentian String atau Akhir Baris dengan Benar
Kode ini tidak menangani string yang diakhiri NULL atau karakter akhir baris. Fungsi ini isspace()
kembali true
untuk akhir baris sehingga kode akan berjalan melewatinya.
while (isspace(**str))
(*str)++;
if (**str == '+' || **str == '-')
{
if (**str == '-')
sign = -1.0;
(*str)++;
}
if (!isdigit(**str))
{
errno = EIO;
return (-1);
}
Kompleksitas
Saya yakin bahwa Anda tidak meminta ini untuk ditinjau, tetapi kompleksitas setiap if
pernyataan dalam contoh fungsi panggilan terlalu banyak dan menyebabkan saya membuat kesalahan dalam tinjauan saya sebelumnya:
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);
}
Saya akan menulis ulang kode sebagai:
#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);
}
yang benar-benar menunjukkan kompleksitas fungsi tersebut.
Memilih
EIO
pelaporan kesalahan sangat meragukan.lc_atof
tidak melakukan input atau output apa pun; mengapa harus melaporkan kesalahan IO? Jika tipe kembalian tidak dapat mewakili hasil (misalnyad_nbr > FLT_MAX
), pilihan logisnya adalahERANGE
atauEOVERFLOW
. Jika konversi tidak dapat diselesaikan karena format argumen yang salah (misalnya!isdigit(**str)
), pilihan logisnya mungkinEINVAL
.Karena itu, saya tidak mendukung pengaturan
errno
di fungsi perpustakaan. Tradisi lama adalah mengaturerrno
dalam panggilan sistem saja. Saya tahu bahwa tradisi ini semakin banyak dilanggar akhir-akhir ini, tapi tetap saja. Jika Anda memiliki cara lain untuk melaporkan kesalahan, tetaplah menggunakannya.Menggunakan parameter inout (
str
dalam kasus Anda) tidak disarankan. Ini tidak perlu memperumit kode, baik di sisi pemanggil dan sisi callee. Callee dipaksa untuk menggunakan tipuan ekstra terlalu banyak, dan khawatir tentang tanda kurung(**str)++
. Pada gilirannya, pemanggil kehilangan jejak di mana parsable dimulai (katakanlah, ia perlu mencatat nomor yang salah format). Lihat bagaimanastrtof
menangani ini:float strtof(const char *restrict nptr, char **restrict endptr);
Berikut
nptr
adalah in-only, danendptr
out-only.Saya terkejut bahwa Anda memutuskan untuk membatasi kegunaan fungsi dengan menangani hanya satu digit setelah titik desimal. Ini bukanlah upaya yang besar untuk menangani semuanya, dan manfaatnya jauh lebih besar.
Tidak perlu memberi tanda kurung nilai yang dikembalikan.
return
adalah operator, bukan fungsi.