简体   繁体   English

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

[英]How do I make sure that strtol() have returned successfully?

according documentation:

On success, the function returns the converted integral number as a long int value.成功时,该函数将转换后的整数作为 long int 值返回。 If no valid conversion could be performed, a zero value is returned.如果无法执行有效转换,则返回零值。 If the correct value is out of the range of representable values, LONG_MAX or LONG_MIN is returned, and the global variable errno is set to ERANGE.如果正确值超出可表示值的范围,则返回 LONG_MAX 或 LONG_MIN,并将全局变量 errno 设置为 ERANGE。

Consider strtol(str, (char**)NULL, 10);考虑strtol(str, (char**)NULL, 10); if str is "0\\0" how to know if the function failed or only has converted the string with "0" number?如果str"0\\0"如何知道函数是否失败或只转换了带有"0"数字的字符串?

You need to pass a real pointer address if you want error checking, so you can distinguish 0 values arising from "0" and similar from 0 values arising from "pqr" :如果你想进行错误检查,你需要传递一个真实的指针地址,这样你就可以区分由"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

Since the accepted answer is not actually a correct way to check for failure.由于接受的答案实际上并不是检查失败的正确方法。

You should not check for errors by examining the return value of strtol, because the string might be a valid representation of 0l, LONG_MAX, or LONG_MIN.您不应通过检查 strtol 的返回值来检查错误,因为该字符串可能是 0l、LONG_MAX 或 LONG_MIN 的有效表示。 Instead, check whether tailptr points to what you expect after the number (eg '\\0' if the string should end after the number).相反,检查 tailptr 是否指向您期望的数字后(例如,如果字符串应在数字后结束,则为 '\\0')。 You also need to clear errno before the call and check it afterward, in case there was overflow.您还需要在调用之前清除 errno 并在调用之后检查它,以防出现溢出。

IMHO, I prefer sscanf() to atoi() or strtol() .恕我直言,我更喜欢sscanf()atoi()strtol() The main reason is that you cannot reliably check for error status on some platforms (ie Windows) unless you use sscanf() (which returns 1 if you succeed, and 0 if you fail).主要原因是您无法在某些平台(即 Windows)上可靠地检查错误状态,除非您使用sscanf() (如果成功则返回1 ,如果失败则返回0 )。

I think I checked all the edge cases.我想我检查了所有的边缘情况。 If anyone thinks of an edge case that I missed please let me know in the comments and I will update this post.如果有人想到我遗漏的边缘情况,请在评论中告诉我,我会更新这篇文章。 I tried to keep the error messages simple.我试图使错误消息保持简单。 If you disagree with this decision please feel free to change them as you see fit.如果您不同意此决定,请随时按照您认为合适的方式进行更改。

// 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");
}

If you object to returning a struct of this size by value then pass a pointer to the struct as an additional argument, just don't forget to handle the case where the struct pointer is NULL .如果您反对按值返回此大小的结构,然后将指向该结构的指针作为附加参数传递,请不要忘记处理结构指针为NULL My goal was to make this code easy to understand.我的目标是使这段代码易于理解。 You probbaly want combine checks and centralize setting the the return values.您可能希望结合检查并集中设置返回值。

Other Thoughts其他想法

I kinda wish strtol() would also return where the number starts.我有点希望strtol()也能返回数字开始的地方。 You could figure this out by iterating back from the end pointer but it might be slow.您可以通过从结束指针返回来解决这个问题,但它可能会很慢。

You can either check the errno or pass a non-NULL value for the second argument and compare its resulting value to str , like:您可以检查errno或为第二个参数传递一个非 NULL 值并将其结果值与str进行比较,例如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在变量上使用 fgetc(stdin) 后,如何将输入传递给 strtol? - After using fgetc(stdin) on a variable, how do I pass the input to strtol? 如何在 C 编程中使用 Strtol 将包含逗号的整数与文本文件分开? - How do I separate integers that contain commas from a text file using Strtol in C programming? 如何在 C 中使用 strtol 将命令行中的数字转换为 inter? - How do I convert a number from the command line to an inter using strtol in C? 如何修改我的代码以使用 strtol() 而不是 scanf() 仅读取前两个整数? - How do I modify my code to use strtol() instead of scanf() to read in first two ints only? 如何确保仅使用我的origninal客户端向我的服务器发出请求? - How do I make sure only my origninal client is used to make requests to my server? 你如何在const-correctness下实现strtol? - How do you implement strtol under const-correctness? 如何检查文件是否已成功写入? - How do I check if file was written to successfully? 如何确保递归终止? - How can I make sure that the recursion terminates? 如何解析整数但使用strtol将“0”保留为有效值? - How can I parse an integer but keep “0” as a valid value using strtol? 我如何确保生产者线程和消费者线程都无限运行并且一个接一个地运行? - How do i make sure that both Producer and Consumer threads run infinitely and one after another?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM