C `strtoul()`very的正确返回值是多少?';“否定”;串?

C `strtoul()`very的正确返回值是多少?';“否定”;串?,c,C,这是一个很好的例子。 我研究了这个问题,发现结果很奇怪,并公布了我的发现 “非常负”字符串应该从strtoul():1,ULONG_MAX返回什么值 strtol() 对于表示数值的字符串,如“123”,strtol()的行为与预期一致。字符串(蓝色)[LONG\u MIN…LONG\u MAX]转换为预期的LONG值。LONG\u MAX上方的字符串(黄色)转换为LONG\u MAX并设置errno==ERANGE。LONG\u MIN下面的字符串转换为LONG\u MIN并设置errno

这是一个很好的例子。 我研究了这个问题,发现结果很奇怪,并公布了我的发现


“非常负”字符串应该从
strtoul()
1
ULONG_MAX
返回什么值


strtol()

对于表示数值的字符串,如
“123”
strtol()
的行为与预期一致。字符串(蓝色)
[LONG\u MIN…LONG\u MAX]
转换为预期的
LONG
值。
LONG\u MAX
上方的字符串(黄色)转换为
LONG\u MAX
并设置
errno==ERANGE
LONG\u MIN
下面的字符串转换为
LONG\u MIN
并设置
errno==ERANGE

strtoul()
positive

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

long strtol_test(const char *s, int base) {
  printf("\n");
  int width = snprintf(NULL, 0, "%ld", LONG_MIN);
  printf("base:%2d \"%s\"\n", base, s);

  char *endptr_signed;
  errno = 0;
  long val_signed = strtol(s, &endptr_signed, base);
  int errno_signed = errno;

  char *endptr_unsigned;
  errno = 0;
  unsigned long val_unsigned = strtoul(s, &endptr_unsigned, base);
  int errno_unsigned = errno;

  if (val_signed < 0 || (unsigned long) val_signed != val_unsigned
      || endptr_signed != endptr_unsigned || errno_signed != errno_unsigned) {
    printf("  signed  val:%*ld end:%2td e:%s\n", width, val_signed,
        endptr_signed - s, strerror(errno_signed));
    printf("unsigned  val:%*lu end:%2td e:%s\n", width, val_unsigned,
        endptr_unsigned - s, strerror(errno_unsigned));
    return 1;
  }

  printf("    both  val:%*ld end:%2td e:%s\n", width, val_signed,
      endptr_signed - s, strerror(errno_signed));
  return 0;
}

int main() {
  char s[][50] = {"-ULONG_MAX1", "-ULONG_MAX", "LONG_MIN1", "LONG_MIN", "-1",
      "-0", "42", "LONG_MAX", "LONG_MAX1", "ULONG_MAX", "ULONG_MAX1", "x"};
  snprintf(s[0], sizeof *s, "-%lu", ULONG_MAX);
  s[0][strlen(s[0]) - 1]++;
  snprintf(s[1], sizeof *s, "-%lu", ULONG_MAX);
  snprintf(s[2], sizeof *s, "%ld", LONG_MIN);
  s[2][strlen(s[2]) - 1]++;
  snprintf(s[3], sizeof *s, "%ld", LONG_MIN);

  snprintf(s[7], sizeof *s, "%ld", LONG_MAX);
  snprintf(s[8], sizeof *s, "%ld", LONG_MAX);
  s[8][strlen(s[8]) - 1]++;
  snprintf(s[9], sizeof *s, "%lu", ULONG_MAX);
  snprintf(s[10], sizeof *s, "%lu", ULONG_MAX);
  s[10][strlen(s[10]) - 1]++;

  strcpy(s[11], s[0]);
  s[11][strlen(s[11]) - 1]++;

  int n = sizeof s / sizeof s[0];
  for (int i = 0; i < n; i++) {
    strtol_test(s[i], 0);
  }
}
对于表示数值的字符串,如
“123”
strtoul()
,其行为也符合预期。字符串(红色)
[0…ULONG_MAX]
转换为预期的
无符号长字符串值。
ULONG_MAX
上方的字符串(绿色)转换为
ULONG_MAX
并设置
errno==ERANGE

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

strtoul()

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

long strtol_test(const char *s, int base) {
  printf("\n");
  int width = snprintf(NULL, 0, "%ld", LONG_MIN);
  printf("base:%2d \"%s\"\n", base, s);

  char *endptr_signed;
  errno = 0;
  long val_signed = strtol(s, &endptr_signed, base);
  int errno_signed = errno;

  char *endptr_unsigned;
  errno = 0;
  unsigned long val_unsigned = strtoul(s, &endptr_unsigned, base);
  int errno_unsigned = errno;

  if (val_signed < 0 || (unsigned long) val_signed != val_unsigned
      || endptr_signed != endptr_unsigned || errno_signed != errno_unsigned) {
    printf("  signed  val:%*ld end:%2td e:%s\n", width, val_signed,
        endptr_signed - s, strerror(errno_signed));
    printf("unsigned  val:%*lu end:%2td e:%s\n", width, val_unsigned,
        endptr_unsigned - s, strerror(errno_unsigned));
    return 1;
  }

  printf("    both  val:%*ld end:%2td e:%s\n", width, val_signed,
      endptr_signed - s, strerror(errno_signed));
  return 0;
}

int main() {
  char s[][50] = {"-ULONG_MAX1", "-ULONG_MAX", "LONG_MIN1", "LONG_MIN", "-1",
      "-0", "42", "LONG_MAX", "LONG_MAX1", "ULONG_MAX", "ULONG_MAX1", "x"};
  snprintf(s[0], sizeof *s, "-%lu", ULONG_MAX);
  s[0][strlen(s[0]) - 1]++;
  snprintf(s[1], sizeof *s, "-%lu", ULONG_MAX);
  snprintf(s[2], sizeof *s, "%ld", LONG_MIN);
  s[2][strlen(s[2]) - 1]++;
  snprintf(s[3], sizeof *s, "%ld", LONG_MIN);

  snprintf(s[7], sizeof *s, "%ld", LONG_MAX);
  snprintf(s[8], sizeof *s, "%ld", LONG_MAX);
  s[8][strlen(s[8]) - 1]++;
  snprintf(s[9], sizeof *s, "%lu", ULONG_MAX);
  snprintf(s[10], sizeof *s, "%lu", ULONG_MAX);
  s[10][strlen(s[10]) - 1]++;

  strcpy(s[11], s[0]);
  s[11][strlen(s[11]) - 1]++;

  int n = sizeof s / sizeof s[0];
  for (int i = 0; i < n; i++) {
    strtol_test(s[i], 0);
  }
}
对于字符串(红色)
[-ULONG_MAX…-1]
,转换将否定正转换并添加
ULONG_MAX+1
(类似于将负值指定给无符号字符串的典型情况),并且不会设置
errno
。这有点令人惊讶,但规范就是这样定义的

strtoul()
非常负面-问题

对于小于
-ULONG_MAX
的字符串(绿色),我希望转换可以像上面描述的较小负值一样处理:转换将否定正转换(
ULONG_MAX
由于溢出),并添加
ULONG_MAX+1
。预期结果
1
(或者可能
0
)为
errno==ERANGE
。然而,练习strtoul()
会导致
ULONG\u MAX

什么是正确的

测试代码

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

long strtol_test(const char *s, int base) {
  printf("\n");
  int width = snprintf(NULL, 0, "%ld", LONG_MIN);
  printf("base:%2d \"%s\"\n", base, s);

  char *endptr_signed;
  errno = 0;
  long val_signed = strtol(s, &endptr_signed, base);
  int errno_signed = errno;

  char *endptr_unsigned;
  errno = 0;
  unsigned long val_unsigned = strtoul(s, &endptr_unsigned, base);
  int errno_unsigned = errno;

  if (val_signed < 0 || (unsigned long) val_signed != val_unsigned
      || endptr_signed != endptr_unsigned || errno_signed != errno_unsigned) {
    printf("  signed  val:%*ld end:%2td e:%s\n", width, val_signed,
        endptr_signed - s, strerror(errno_signed));
    printf("unsigned  val:%*lu end:%2td e:%s\n", width, val_unsigned,
        endptr_unsigned - s, strerror(errno_unsigned));
    return 1;
  }

  printf("    both  val:%*ld end:%2td e:%s\n", width, val_signed,
      endptr_signed - s, strerror(errno_signed));
  return 0;
}

int main() {
  char s[][50] = {"-ULONG_MAX1", "-ULONG_MAX", "LONG_MIN1", "LONG_MIN", "-1",
      "-0", "42", "LONG_MAX", "LONG_MAX1", "ULONG_MAX", "ULONG_MAX1", "x"};
  snprintf(s[0], sizeof *s, "-%lu", ULONG_MAX);
  s[0][strlen(s[0]) - 1]++;
  snprintf(s[1], sizeof *s, "-%lu", ULONG_MAX);
  snprintf(s[2], sizeof *s, "%ld", LONG_MIN);
  s[2][strlen(s[2]) - 1]++;
  snprintf(s[3], sizeof *s, "%ld", LONG_MIN);

  snprintf(s[7], sizeof *s, "%ld", LONG_MAX);
  snprintf(s[8], sizeof *s, "%ld", LONG_MAX);
  s[8][strlen(s[8]) - 1]++;
  snprintf(s[9], sizeof *s, "%lu", ULONG_MAX);
  snprintf(s[10], sizeof *s, "%lu", ULONG_MAX);
  s[10][strlen(s[10]) - 1]++;

  strcpy(s[11], s[0]);
  s[11][strlen(s[11]) - 1]++;

  int n = sizeof s / sizeof s[0];
  for (int i = 0; i < n; i++) {
    strtol_test(s[i], 0);
  }
}

对于
strtoul()
,范围错误结果始终为
ULONG\u MAX

这相当简单,对于像
“-18446744073709551616”
这样的字符串,由于正确的(正)值超出范围,因此返回
ULONG_MAX
,并将
errno
设置为
ERANGE
。对于提供任何其他答案的
strtoul()
范围错误,不存在任何规定

。。。如果正确值超出可表示值的范围<代码>ULONG_MAX
。。。返回(根据返回类型和值的符号,如果有),宏
ERANGE
的值存储在
errno
中。C17dr§7.22.1.4 8


因此,当字符串在
[-ULONG\u MAX…+ULONG\u MAX]
范围内时,
strtoul()
生成一个非错误值。转换后的结果是一个无符号长
[0…ULONG_MAX]
。错误结果总是
ULONG\u MAX

对于
strtoul()
,范围错误结果总是
ULONG\u MAX

这相当简单,对于像
“-18446744073709551616”
这样的字符串,由于正确的(正)值超出范围,因此返回
ULONG_MAX
,并将
errno
设置为
ERANGE
。对于提供任何其他答案的
strtoul()
范围错误,不存在任何规定

。。。如果正确值超出可表示值的范围<代码>ULONG_MAX。。。返回(根据返回类型和值的符号,如果有),宏
ERANGE
的值存储在
errno
中。C17dr§7.22.1.4 8

因此,当字符串在
[-ULONG\u MAX…+ULONG\u MAX]
范围内时,
strtoul()
生成一个非错误值。转换后的结果是一个无符号长
[0…ULONG_MAX]
。错误结果总是
ULONG\u MAX