我目前正在编写一个将字符串转换为double longs的函数。我知道已经有一个功能可以做到这一点。我只是为练习写这个功能,但我现在不知道该怎么做。
当我的函数只能处理正双倍长度时,我的函数正常工作,因为如果它们是字符串中的无效字符,我可以返回-1。但是我希望这个函数能够处理负双长以及正负。我不知道在这种情况下我应该做什么,因为现在所有实数都是有效的返回值。我想通过将无效字符转换为十进制值或者只是忽略无效字符并仅选择有效字符0-9(十进制48-57)来继续转换,即使存在无效字符。我还考虑过返回一个指向double long的指针并使用NULL地址来表示找到了无效字符,或者我可以像函数errno
那样设置readdir()
。我不确定如何设置errno
或者是否允许设置$&3%7AJ89
。所以我的问题总体上是你们建议我在这种情况下做什么?另请注意,我还没有包含负双头长度的处理,该功能只会忽略无效字符:例如double long cstrtodl(const char *cstr)
{
double long power;
double long dl = 0;
int decimal_place;
int bool_decimal = 0;
for(decimal_place = 1; cstr[decimal_place] != '\0'; decimal_place++)
{
if(cstr[decimal_place] == '.')
{
bool_decimal = decimal_place;
break;
}
}
for(decimal_place--, power = 1; decimal_place >= 0; decimal_place--, power *= 10)
{
printf("[%i] = %i(%c)\nPOWER = %LF\nINTEGER = %LF\n", decimal_place, (int)cstr[decimal_place], cstr[decimal_place], power, dl);
switch(cstr[decimal_place])
{
case 48:
dl += 0 * power;
break;
case 49:
dl += 1 * power;
break;
case 50:
dl += 2 * power;
break;
case 51:
dl += 3 * power;
break;
case 52:
dl += 4 * power;
break;
case 53:
dl += 5 * power;
break;
case 54:
dl += 6 * power;
break;
case 55:
dl += 7 * power;
break;
case 56:
dl += 8 * power;
break;
case 57:
dl += 9 * power;
break;
default:
power /= 10;
break;
}
}
if(bool_decimal > 0)
{
for(decimal_place = bool_decimal+1, power = 10; cstr[decimal_place] != '\0'; decimal_place++, power *= 10)
{
printf("[%i] = %i(%c)\nPOWER = %LF\nINTEGER = %LF\n", decimal_place, (int)cstr[decimal_place], cstr[decimal_place], power, dl);
switch(cstr[decimal_place])
{
case 48:
dl += 0 / power;
break;
case 49:
dl += 1 / power;
break;
case 50:
dl += 2 / power;
break;
case 51:
dl += 3 / power;
break;
case 52:
dl += 4 / power;
break;
case 53:
dl += 5 / power;
break;
case 54:
dl += 6 / power;
break;
case 55:
dl += 7 / power;
break;
case 56:
dl += 8 / power;
break;
case 57:
dl += 9 / power;
break;
default:
power /= 10;
break;
}
}
}
return dl;
}
将转换为3789。
{{1}}
答案 0 :(得分:5)
返回一个指针是复杂而低效的,因为你必须malloc
一个缓冲区,然后记住free
它。这对于返回一个简单的固定大小的值来说是一个很大的开销。相反,您可以返回状态代码并将结果写入指针:
// returns zero on success, -1 on error
int cstrtodl(const char *cstr, long double *result);
出于某些目的,如果您不一定阅读所有字符串,知道消耗了多少字符串也可能很有用。在这种情况下,您可以返回size_t
或ssize_t
,并返回0(没有输入消耗)或-1时出错。然后,调用者可以检查输入字符串中的数字是否出现意外情况。
在C中完全允许设置errno
。
答案 1 :(得分:4)
您有几种选择:
使用NAN作为错误值。请注意,您无法使用==
检查NAN。您必须使用isnan(x)
或x!=x
来检查x
是否为NAN。
添加额外的int *errorp
参数来存储是否发生错误的标志(可能还有错误代码)。此选项有两个子选项:您可能希望在成功时写入0,或者您可能希望单独保留先前的内容,以便调用者可以进行多次调用,并且仅在最后检查是否其中任何一个失败
切换:传递指向存储结果的位置的指针,并使用返回值作为错误代码。这种方法鼓励在每次调用后检查错误,但是很难直接在表达式中使用结果,这可能很烦人。
通过特殊的线程本地状态报告错误:errno
,浮点异常标志(警告:某些机器不支持fenv / exceptions!),或者您自己的线程本地对象。根据您的观点,这可能是错误的/丑陋的,因为它是一个隐藏的信息渠道,但它也可以是调用者最方便的。
通过全局状态报告错误。请永远不要这样做。它排除了代码的多线程和干净库的使用。
答案 2 :(得分:2)
errno
可用于此目的。您需要#include <errno.h>
才能使用它。您将errno
设置为某个预定义值,然后调用者将其检查:
errno = 0;
cstrtodl(some_string);
if (errno != 0) {
// Error occured.
}
请注意errno
是线程安全的。如果在一个线程中写入它,则其他线程中的值不会更改。所以它不仅仅是一些全局变量。它甚至可以不是变量,而是内部编译器魔法。但问题是,您可以将其视为线程局部变量。
另一种方法是在额外的参数中传递错误:
double long cstrtodl(const char *cstr, int *error)
{
// ...
if (error != NULL) {
if (some_error_occured) {
*error = SOME_CONSTANT_OR_MACRO;
} else {
*error = 0;
}
}
}
然后,呼叫者可以这样做:
int error;
cstrtodl(some_string, &error);
if (*error) {
// Error occured.
}
或者,如果来电者不感兴趣:
cstrtodl(some_string, NULL);
答案 3 :(得分:1)
我建议返回指向结果double的指针或错误时返回NULL(如你所建议的那样),或者作为strtoul(),添加指向第一个未转换字符的指针参数。
至于errno,它只是另一个全局变量(恰好在libc中声明)并且可以为它分配自己的值。