如何确保 strtol() 已成功返回?

Posted

技术标签:

【中文标题】如何确保 strtol() 已成功返回?【英文标题】:How do I make sure that strtol() have returned successfully? 【发布时间】:2012-07-02 01:07:00 【问题描述】:

according documentation:

成功时,函数将转换后的整数作为 长整数值。如果不能执行有效的转换,则为零 返回值。如果正确的值超出范围 可表示的值,返回 LONG_MAX 或 LONG_MIN,并且全局 变量 errno 设置为 ERANGE。

考虑strtol(str, (char**)NULL, 10); 如果str"0\0" 如何知道函数是失败还是只转换了带有"0" 数字的字符串?

【问题讨论】:

@StevenLuu: scanf 的错误处理更糟糕。一些实现通常会在溢出时给你垃圾,但不会以任何方式发出错误信号。 【参考方案1】:

我想我检查了所有的边缘情况。如果有人想到我错过的边缘情况,请在 cmets 中告诉我,我会更新这篇文章。我试图使错误消息保持简单。如果您不同意此决定,请随时根据需要进行更改。

// Copyright (C) 2021 by cmwt
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <errno.h>    // errno, ERANGE
#include <limits.h>   // LONG_MAX, LONG_MIN
#include <stdbool.h>  // true, false
#include <stddef.h>   // ptrdiff_t
#include <stdio.h>    // printf()
#include <stdlib.h>   // strtol()

struct ResultToLong 
  const char *err_msg;
  long        answer;
  ptrdiff_t   num_read;
  bool        is_func_success;
;

struct ResultToLong stringToLong(const char* start, int base) 
  struct ResultToLong result = NULL, 0, 0, false;
  int save_errno = 0;
  char *end = NULL;

  if (base < 0 || base > 36) 
    result.err_msg = "Bad base: expect (0 <= base <= 36)";
    return result;
  
  if (start == NULL) 
    result.err_msg = "Bad start: expect (start != NULL)";
    return result;
  
  if (*start == '\0') 
    result.err_msg = "Bad start: start empty (const char* start == \"\";)";
    return result;
  

  errno = 0;
  result.answer = strtol(start, &end, base);
  save_errno = errno;

  if (result.answer == 0 && *(start - 1) != '0') 
    result.err_msg = "Bad start: not a number";
    result.num_read = end - start;
    return result;
  
  if (result.answer == LONG_MIN && save_errno == ERANGE) 
    result.err_msg = "Bad start: result < LONG_MIN";
    result.num_read = end - start;
    return result;
  
  if (result.answer == LONG_MAX && save_errno == ERANGE) 
    result.err_msg = "Bad start: result > LONG_MAX";
    result.num_read = end - start;
    return result;
  
  if (*end != '\0') 
  result.err_msg = "Warning: number in start is not '\\0' terminated";
  result.num_read = end - start;
  result.is_func_success = true;
  return result;
  

  result.err_msg = "Success";
  result.num_read = end - start;
  result.is_func_success = true;
  return result;


int main() 
  struct ResultToLong result;
  const char* str;
  
  printf("Starting...\n\n");

  str= NULL;
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: %s\n", "<NULL>");
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);
  
  str= "";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "42";
  result = stringToLong(str, -1);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "42";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "42 ";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "                42";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "0x42";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "042";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "+9999999999999999999";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "-9999999999999999999";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "?";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  printf("Done.\n");

如果您反对按值返回此大小的结构,则将指向该结构的指针作为附加参数传递,只是不要忘记处理结构指针为NULL 的情况。我的目标是让这段代码易于理解。您可能希望合并检查并集中设置返回值。

其他想法

我有点希望strtol() 也能返回数字开始的位置。您可以通过从结束指针迭代回来来解决这个问题,但它可能会很慢。

【讨论】:

【参考方案2】:

因为接受的答案实际上并不是检查失败的正确方法。

您不应通过检查 strtol 的返回值来检查错误,因为该字符串可能是 0l、LONG_MAX 或 LONG_MIN 的有效表示。相反,请检查 tailptr 是否指向您期望的数字之后的内容(例如,如果字符串应该在数字之后结束,则为 '\0')。您还需要在调用之前清除 errno 并 之后检查一下,以防溢出。

【讨论】:

【参考方案3】:

恕我直言,我更喜欢sscanf() 而不是atoi()strtol()。主要原因是您无法可靠地检查某些平台(即 Windows)上的错误状态除非您使用 sscanf()(如果成功则返回 1,并且0 如果你失败了)。

【讨论】:

为什么我不能在某些平台上检查错误状态?恕我直言:strtol() 可能会在失败时返回负值.. @Jack longs 也可以是负数。 scanf 不会在溢出时返回失败。使用strtol 很容易转换失败,只有检查溢出很烦人。 您能否详细说明原因,例如,Daniel Fischer 的解决方案在 Windows 上不起作用?与 MSDN 文档相反,似乎 MSC 运行时 errno 实现线程安全的,请参阅 ***.com/questions/6413052/msvc-errno-thread-safety【参考方案4】:

如果要进行错误检查,则需要传递一个真实的指针地址,以便区分"0"产生的0值和"pqr"产生的0值:

char *endptr;
errno = 0;
long result = strtol(str, &endptr, 10);
if (endptr == str)

    // nothing parsed from the string, handle errors or exit

if ((result == LONG_MAX || result == LONG_MIN) && errno == ERANGE)

    // out of range, handle or exit

// all went fine, go on

【讨论】:

丹尼尔:我需要问你几件事。可以来this chat吗? 为什么不直接if (errno == ERANGE) 来检查失败? @Undefined 好问题。我认为 [模糊记忆] 单独检查 ERANGE 有问题是有原因的,the man page 中的示例代码也与 ERANGE 检查一起进行值检查。但我已经完全忘记了那个原因是什么。如果确实有原因。 这实际上不是检查失败的正确方法。您不应通过检查 strtol 的返回值来检查错误,因为字符串可能是 0l、LONG_MAX 或 LONG_MIN 的有效表示。相反,请检查 tailptr 是否指向您期望的数字之后的内容(例如,如果字符串应该在数字之后结束,则为 '\0')。你还需要在调用前清除errno,然后再检查,以防溢出 但是人们并不总是知道在@малинчекуров 之后会发生什么,那么如何检查它是否指向人们所期望的呢?【参考方案5】:

您可以检查errno 或为第二个参数传递一个非NULL 值并将其结果值与str 进行比较,例如:

char * endptr;
long result = strtol(str, &endptr, 10);
if (endptr > str)

    // Use result...

【讨论】:

以上是关于如何确保 strtol() 已成功返回?的主要内容,如果未能解决你的问题,请参考以下文章

在 python 中使用 zip() 时如何确保所有文件都已成功压缩,是不是有任何功能可以检查或确认?

如何确保查询成功执行

Strtol 第二个参数

将 Long 转换为 Unsigned Short Int / Strtol

如何确保JavaScript的执行顺序

JavaWeb学习 : 如何在 Eclipse 中创建一个Web 项目并成功运行?