`strtoul()` 对于非常“负”的字符串,正确的返回值是什么?

chu*_*ica 2 c

这是一个“可以提出并回答你自己的问题”。\n我研究了这个问题,发现结果很好奇,并发布了我的发现。

\n\n
\n\n

strtoul()“非常负”的字符串应该从: 1,返回什么值ULONG_MAX或者什么?

\n\n
\n\n

strtol()

\n\n

对于表示数值的字符串,如"123",strtol()的行为符合预期。字符串(蓝色)[LONG_MIN ... LONG_MAX]转换为其预期long值。上面的字符串(黄色)LONG_MAX转换为LONG_MAX并设置errno==ERANGE。下面的字符串LONG_MIN转换为LONG_MIN并设置errno==ERANGE

\n\n

strtoul()积极的

\n\n

对于表示数值的字符串,如"123", strtoul()也按预期运行。字符串(红色)[0 ... ULONG_MAX]转换为其预期unsigned long值。上面的字符串(绿色)ULONG_MAX转换为ULONG_MAX并设置errno==ERANGE

\n\n
\n

如果主题序列以减号开头,则转换产生的值将被取反(在返回类型中)。C17dr \xc2\xa7 7.22.1.4 5

\n
\n\n

strtoul()消极的

\n\n

对于字符串(红色)[-ULONG_MAX ... -1],转换会否定正转换并添加ULONG_MAX + 1(就像将负值分配给无符号的典型操作一样)并且不会设置errno。这有点令人惊讶,但这就是规范的定义方式。

\n\n

strtoul()非常消极 - 问题

\n\n

对于小于 的字符串(绿色)-ULONG_MAX,我希望像上述较小的负值一样处理转换:转换否定正转换(ULONG_MAX由于溢出)并添加ULONG_MAX + 1。预期结果1(或可能0errno == ERANGE。然而锻炼却strtoul()带来了ULONG_MAX

\n\n

什么是正确的?

\n\n

斯特图勒

\n\n

测试代码

\n\n
#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\nlong strtol_test(const char *s, int base) {\n  printf("\\n");\n  int width = snprintf(NULL, 0, "%ld", LONG_MIN);\n  printf("base:%2d \\"%s\\"\\n", base, s);\n\n  char *endptr_signed;\n  errno = 0;\n  long val_signed = strtol(s, &endptr_signed, base);\n  int errno_signed = errno;\n\n  char *endptr_unsigned;\n  errno = 0;\n  unsigned long val_unsigned = strtoul(s, &endptr_unsigned, base);\n  int errno_unsigned = errno;\n\n  if (val_signed < 0 || (unsigned long) val_signed != val_unsigned\n      || endptr_signed != endptr_unsigned || errno_signed != errno_unsigned) {\n    printf("  signed  val:%*ld end:%2td e:%s\\n", width, val_signed,\n        endptr_signed - s, strerror(errno_signed));\n    printf("unsigned  val:%*lu end:%2td e:%s\\n", width, val_unsigned,\n        endptr_unsigned - s, strerror(errno_unsigned));\n    return 1;\n  }\n\n  printf("    both  val:%*ld end:%2td e:%s\\n", width, val_signed,\n      endptr_signed - s, strerror(errno_signed));\n  return 0;\n}\n\nint main() {\n  char s[][50] = {"-ULONG_MAX1", "-ULONG_MAX", "LONG_MIN1", "LONG_MIN", "-1",\n      "-0", "42", "LONG_MAX", "LONG_MAX1", "ULONG_MAX", "ULONG_MAX1", "x"};\n  snprintf(s[0], sizeof *s, "-%lu", ULONG_MAX);\n  s[0][strlen(s[0]) - 1]++;\n  snprintf(s[1], sizeof *s, "-%lu", ULONG_MAX);\n  snprintf(s[2], sizeof *s, "%ld", LONG_MIN);\n  s[2][strlen(s[2]) - 1]++;\n  snprintf(s[3], sizeof *s, "%ld", LONG_MIN);\n\n  snprintf(s[7], sizeof *s, "%ld", LONG_MAX);\n  snprintf(s[8], sizeof *s, "%ld", LONG_MAX);\n  s[8][strlen(s[8]) - 1]++;\n  snprintf(s[9], sizeof *s, "%lu", ULONG_MAX);\n  snprintf(s[10], sizeof *s, "%lu", ULONG_MAX);\n  s[10][strlen(s[10]) - 1]++;\n\n  strcpy(s[11], s[0]);\n  s[11][strlen(s[11]) - 1]++;\n\n  int n = sizeof s / sizeof s[0];\n  for (int i = 0; i < n; i++) {\n    strtol_test(s[i], 0);\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

样本输出

\n\n
base: 0 "-18446744073709551616"\n  signed  val:-9223372036854775808 end:21 e:Numerical result out of range\nunsigned  val:18446744073709551615 end:21 e:Numerical result out of range\n\nbase: 0 "-18446744073709551615"\n  signed  val:-9223372036854775808 end:21 e:Numerical result out of range\nunsigned  val:                   1 end:21 e:No error\n\nbase: 0 "-9223372036854775809"\n  signed  val:-9223372036854775808 end:20 e:Numerical result out of range\nunsigned  val: 9223372036854775807 end:20 e:No error\n\nbase: 0 "-9223372036854775808"\n  signed  val:-9223372036854775808 end:20 e:No error\nunsigned  val: 9223372036854775808 end:20 e:No error\n\nbase: 0 "-1"\n  signed  val:                  -1 end: 2 e:No error\nunsigned  val:18446744073709551615 end: 2 e:No error\n\nbase: 0 "-0"\n    both  val:                   0 end: 2 e:No error\n\nbase: 0 "42"\n    both  val:                  42 end: 2 e:No error\n\nbase: 0 "9223372036854775807"\n    both  val: 9223372036854775807 end:19 e:No error\n\nbase: 0 "9223372036854775808"\n  signed  val: 9223372036854775807 end:19 e:Numerical result out of range\nunsigned  val: 9223372036854775808 end:19 e:No error\n\nbase: 0 "18446744073709551615"\n  signed  val: 9223372036854775807 end:20 e:Numerical result out of range\nunsigned  val:18446744073709551615 end:20 e:No error\n\nbase: 0 "18446744073709551616"\n  signed  val: 9223372036854775807 end:20 e:Numerical result out of range\nunsigned  val:18446744073709551615 end:20 e:Numerical result out of range\n\nbase: 0 "-18446744073709551617"\n  signed  val:-9223372036854775808 end:21 e:Numerical result out of range\nunsigned  val:18446744073709551615 end:21 e:Numerical result out of range\n
Run Code Online (Sandbox Code Playgroud)\n

chu*_*ica 5

范围错误结果始终ULONG_MAXstrtoul()

\n

strtol()这与返回值为LONG_MINor不同LONG_MAX,具体取决于范围错误的符号。

\n

对于像 之类的字符串来说,这非常简单"-18446744073709551616",因为正确的(正)值超出了范围,ULONG_MAX因此返回并errno设置为ERANGE。对于范围错误,不存在strtoul()提供任何其他答案的规定。

\n
\n

... 如果正确的值超出了可表示值的范围,则ULONG_MAX返回 ... , ... (根据值的返回类型和符号,如果有的话),并且宏的值ERANGE存储在errno. C17dr \xc2\xa7 7.22.1.4 8

\n
\n

因此,strtoul()当字符串位于[-ULONG_MAX ... +ULONG_MAX]范围内时,会生成一个非错误值。转换后的结果是一个unsigned long [0 ... ULONG_MAX]. 错误结果总是ULONG_MAX

\n