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

时间:2012-07-01 05:16:51

标签: c type-conversion

according documentation:

  

成功时,该函数返回转换后的整数作为a   long int值。如果无法执行有效转换,则为零   返回值。如果正确的值超出范围   可表示的值,返回LONG_MAX或LONG_MIN,以及全局   变量errno设置为ERANGE。

如果strtol(str, (char**)NULL, 10);str "0\0"如何知道该功能是否失败或仅仅使用"0"号转换了字符串,请考虑{{1}}

5 个答案:

答案 0 :(得分:11)

如果要进行错误检查,则需要传递一个真实的指针地址,这样就可以区分"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

答案 1 :(得分:2)

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

答案 2 :(得分:2)

由于接受的答案实际上不是检查失败的正确方法。

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

答案 3 :(得分:2)

我想我检查了所有的边缘情况。如果有人想到我遗漏的边缘情况,请在评论中告诉我,我会更新这篇文章。我试图使错误消息保持简单。如果您不同意此决定,请随时按照您认为合适的方式进行更改。

// 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() 也能返回数字开始的地方。您可以通过从结束指针返回来解决这个问题,但它可能会很慢。

答案 4 :(得分:1)

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

char * endptr;
long result = strtol(str, &endptr, 10);
if (endptr > str)
{
    // Use result...
}