C++ strtol 的正确使用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14176123/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:04:23  来源:igfitidea点击:

Correct usage of strtol

c++c

提问by Jimm

The program below converts a string to long, but based on my understanding it also returns an error. I am relying on the fact that if strtolsuccessfully converted string to long, then the second parameter to strtolshould be equal to NULL. When I run the below application with 55, I get the following message.

下面的程序将字符串转换为 long,但根据我的理解,它也会返回错误。我依赖的事实是,如果strtol成功将字符串转换为 long,那么第二个参数 tostrtol应该等于 NULL。当我使用 55 运行以下应用程序时,我收到以下消息。

./convertToLong 55
Could not convert 55 to long and leftover string is: 55 as long is 55

How can I successfully detect errors from strtol? In my application, zero is a valid value.

如何成功检测 strtol 中的错误?在我的应用程序中,零是一个有效值。

Code:

代码:

#include <stdio.h>
#include <stdlib.h>

static long parseLong(const char * str);

int main(int argc, char ** argv)
{
    printf("%s as long is %ld\n", argv[1], parseLong(argv[1]));
    return 0;
 }

static long parseLong(const char * str)
{
    long _val = 0;
    char * temp;

    _val = strtol(str, &temp, 0);

    if(temp != '
if (*temp != '
static long parseLong(const char *str)
{
    errno = 0;
    char *temp;
    long val = strtol(str, &temp, 0);

    if (temp == str || *temp != '
bool parseLong(const char *str, long *val)
{
    char *temp;
    bool rc = true;
    errno = 0;
    *val = strtol(str, &temp, 0);

    if (temp == str || *temp != '
if (parseLong(str, &value))
    …conversion successful…
else
    …handle error…
' || ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE)) rc = false; return rc; }
' || ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE)) fprintf(stderr, "Could not convert '%s' to long and leftover string is: '%s'\n", str, temp); // cerr << "Could not convert '" << str << "' to long and leftover string is '" // << temp << "'\n"; return val; }
')
') printf("Could not convert %s to long and leftover string is: %s", str, temp); return _val; }

采纳答案by chris

You're almost there. tempitself will not be null, but it will point to a null character if the whole string is converted, so you need to dereference it:

您快到了。temp本身不会为空,但如果整个字符串被转换,它将指向空字符,因此您需要取消引用它:

int saved = errno;  // At the start, before errno = 0;

…rest of function…

if (errno == 0)     // Before the return
    errno = saved;

回答by Jonathan Leffler

Note that names beginning with an underscore are reserved for the implementation; it is best to avoid using such names in your code. Hence, _valshould be just val.

请注意,以下划线开头的名称是为实现保留的;最好避免在代码中使用此类名称。因此,_val应该只是val.

The full specification of error handling for strtol()and its relatives is complex, surprisingly complex, when you first run across it. One thing you're doing absolutely right is using a function to invoke strtol(); using it 'raw' in code is probably not correct.

strtol()当您第一次遇到它时,其及其亲属的错误处理的完整规范是复杂的,令人惊讶的复杂。您做的绝对正确的一件事是使用函数来调用strtol(); 在代码中使用它“原始”可能不正确。

Since the question is tagged with both C and C++, I will quote from the C2011 standard; you can find the appropriate wording in the C++ standard for yourself.

由于问题同时标有 C 和 C++,我将引用 C2011 标准;您可以在 C++ 标准中为自己找到合适的措辞。

ISO/IEC 9899:2011 §7.22.1.4 The strtol, strtoll, strtouland strtoullfunctions

long int strtol(const char * restrict nptr, char ** restrict endptr, int base);

?2 [...] First, they decompose the input string into three parts: an initial, possibly empty, sequence of white-space characters (as specified by the isspace function), a subject sequence resembling an integer represented in some radix determined by the value of base, and a final string of one or more unrecognized characters, including the terminating null character of the input string. [...]

?7 If the subject sequence is empty or does not have the expected form, no conversion is performed; the value of nptris stored in the object pointed to by endptr, provided that endptris not a null pointer.

Returns

?8 The strtol, strtoll, strtoul, and strtoullfunctions return the converted value, if any. If no conversion could be performed, zero is returned. If the correct value is outside the range of representable values, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX, or ULLONG_MAX is returned (according to the return type and sign of the value, if any), and the value of the macro ERANGE is stored in errno.

ISO / IEC 9899:2011§7.22.1.4的strtolstrtollstrtoulstrtoull功能

long int strtol(const char * restrict nptr, char ** restrict endptr, int base);

?2 [...] 首先,他们将输入字符串分解为三个部分:一个初始的,可能为空的,空白字符序列(由 isspace 函数指定),一个类似于整数的主题序列,以某些确定的基数表示由 base 的值和一个或多个无法识别的字符组成的最终字符串,包括输入字符串的终止空字符。[...]

?7 如果主题序列为空或不具有预期形式,则不进行转换;的值nptr存储在指向的对象中endptr,前提是endptr它不是空指针。

退货

?8 strtolstrtollstrtoul,和strtoull函数返回转换后的值,如果有的话。如果无法执行转换,则返回零。如果正确的值在可表示值的范围之外,则返回LONG_MIN、LONG_MAX、LLONG_MIN、LLONG_MAX、ULONG_MAX或ULLONG_MAX(根据值的返回类型和符号,如果有),宏ERANGE的值为存储在errno.

Remember that no standard C library function ever sets errnoto 0. Therefore, to be reliable, you must set errnoto zero before calling strtol().

请记住,没有任何标准 C 库函数会设置errno为 0。因此,为了可靠,您必须errno在调用 之前设置为零strtol()

So, your parseLong()function might look like:

因此,您的parseLong()函数可能如下所示:

if (*temp != '
*temp != '
RETURN VALUES
     The strtol(), strtoll(), strtoimax(), and strtoq() functions return the result
     of the conversion, unless the value would underflow or overflow.  If no conver-
     sion could be performed, 0 is returned and the global variable errno is set to
     EINVAL (the last feature is not portable across all platforms).  If an overflow
     or underflow occurs, errno is set to ERANGE and the function return value is
     clamped according to the following table.
'
')

Note that on error, this returns 0 or LONG_MIN or LONG_MAX, depending on what strtol()returned. If your calling code needs to know whether the conversion was successful or not, you need a different function interface — see below. Also, note that errors should be printed to stderrrather than stdout, and error messages should be terminated by a newline \n; if they're not, they aren't guaranteed to appear in a timely fashion.

请注意,出错时,这将返回 0 或 LONG_MIN 或 LONG_MAX,具体取决于strtol()返回的内容。如果你的调用代码需要知道转换是否成功,你需要一个不同的函数接口——见下文。另请注意,错误应打印到stderr而不是stdout,并且错误消息应以换行符终止\n;如果不是,则不能保证它们会及时出现。

Now, in library code you probably do not want any printing, and your calling code might want to know whether the conversion was successful of not, so you might revise the interface too. In that case, you'd probably modify the function so it returns a success/failure indication:

现在,在库代码中,您可能不需要任何打印,并且您的调用代码可能想知道转换是否成功,因此您也可以修改接口。在这种情况下,您可能会修改该函数,使其返回成功/失败指示:

##代码##

which you could use like:

你可以像这样使用:

##代码##

If you need to distinguish between 'trailing junk', 'invalid numeric string', 'value too big' and 'value too small' (and 'no error'), you'd use an integer or enuminstead of a boolean return code. If you want to allow trailing white space but no other characters, or if you don't want to allow any leading white space, you have more work to do in the function. The code allows octal, decimal and hexadecimal; if you want strictly decimal, you need to change the 0 to 10 in the call to strtol().

如果您需要区分“尾随垃圾”、“无效数字字符串”、“值太大”和“值太小”(以及“无错误”),您可以使用整数或enum代替布尔返回码。如果您希望允许尾随空格但不允许其他字符,或者如果您不想允许任何前导空格,则在该函数中还有更多工作要做。代码允许八进制、十进制和十六进制;如果您想要严格的十进制,则需要将调用中的 0 更改为 10 strtol()

If your functions are to masquerade as part of the standard library, they should not set errnoto 0permanently, so you'd need to wrap the code to preserve errno:

如果您的函数要伪装成标准库的一部分,则它们不应设置errno0永久,因此您需要包装代码以保留errno

##代码##

回答by chris

You're missing a level of indirection. You want to check whether the characteris the terminating NUL, and not if the pointer is NULL:

你错过了一个间接级别。您想检查字符是否为终止符NUL,而不是指针是否为NULL

##代码##

By the way, this is not a good approach for error checking. The proper error checking method of the strto*family of functions is not done by comparing the output pointer with the end of the string. It should be done by checking for a zero return value and getting the return value of errno.

顺便说一句,这不是错误检查的好方法。strto*函数族的正确错误检查方法不是通过将输出指针与字符串末尾进行比较来完成的。它应该通过检查零返回值并获取 的返回值来完成errno

回答by spartygw

You should be checking

你应该检查

##代码##

You should also be able to check the value of errno after calling strotol according to this:

您还应该能够在根据此调用 strool 后检查 errno 的值:

##代码##