c - `strtoul()` 对于非常“负”的字符串,正确的返回值是多少?
问题描述
这是一个可以提问和回答自己的问题。我研究了这个问题,发现结果很奇怪,并发布了我的发现。
“非常负面”的字符串应该从strtoul()
:返回什么值1
,ULONG_MAX
或者什么?
strtol()
对于表示数值的字符串,如"123"
, 的strtol()
行为符合预期。字符串(蓝色)[LONG_MIN ... LONG_MAX]
转换为其预期long
值。上面的字符串(黄色)LONG_MAX
转换为LONG_MAX
并设置errno==ERANGE
. 下面的字符串LONG_MIN
转换为LONG_MIN
并设置errno==ERANGE
.
strtoul()
积极的
对于表示数值的字符串,如"123"
, strtoul()
也按预期运行。字符串(红色)[0 ... ULONG_MAX]
转换为其预期unsigned long
值。上面的字符串(绿色)ULONG_MAX
转换为ULONG_MAX
并设置errno==ERANGE
.
如果主题序列以减号开头,则转换产生的值被否定(在返回类型中)。C17dr § 7.22.1.4 5
strtoul()
消极的
对于字符串(红色)[-ULONG_MAX ... -1]
,转换否定正转换并添加ULONG_MAX + 1
(如将负值典型分配给unsigned)并且不设置errno
。这有点令人惊讶,但这就是规范定义它的方式。
strtoul()
非常消极 - 问题
对于小于的字符串(绿色)-ULONG_MAX
,我希望转换像上面描述的较小的负值一样处理:转换否定正转换(ULONG_MAX
由于溢出)并添加ULONG_MAX + 1
. 预期结果1
(或可能0
)与errno == ERANGE
. 然而,锻炼strtoul()
导致ULONG_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);
}
}
样本输出
base: 0 "-18446744073709551616"
signed val:-9223372036854775808 end:21 e:Numerical result out of range
unsigned val:18446744073709551615 end:21 e:Numerical result out of range
base: 0 "-18446744073709551615"
signed val:-9223372036854775808 end:21 e:Numerical result out of range
unsigned val: 1 end:21 e:No error
base: 0 "-9223372036854775809"
signed val:-9223372036854775808 end:20 e:Numerical result out of range
unsigned val: 9223372036854775807 end:20 e:No error
base: 0 "-9223372036854775808"
signed val:-9223372036854775808 end:20 e:No error
unsigned val: 9223372036854775808 end:20 e:No error
base: 0 "-1"
signed val: -1 end: 2 e:No error
unsigned val:18446744073709551615 end: 2 e:No error
base: 0 "-0"
both val: 0 end: 2 e:No error
base: 0 "42"
both val: 42 end: 2 e:No error
base: 0 "9223372036854775807"
both val: 9223372036854775807 end:19 e:No error
base: 0 "9223372036854775808"
signed val: 9223372036854775807 end:19 e:Numerical result out of range
unsigned val: 9223372036854775808 end:19 e:No error
base: 0 "18446744073709551615"
signed val: 9223372036854775807 end:20 e:Numerical result out of range
unsigned val:18446744073709551615 end:20 e:No error
base: 0 "18446744073709551616"
signed val: 9223372036854775807 end:20 e:Numerical result out of range
unsigned val:18446744073709551615 end:20 e:Numerical result out of range
base: 0 "-18446744073709551617"
signed val:-9223372036854775808 end:21 e:Numerical result out of range
unsigned val:18446744073709551615 end:21 e:Numerical result out of range
解决方案
范围错误结果始终ULONG_MAX
为strtoul()
.
对于像 之类的字符串,这相当简单,"-18446744073709551616"
因为正确的(正)值在范围之外,ULONG_MAX
因此返回并errno
设置为ERANGE
。对于范围错误,不存在strtoul()
提供任何其他答案的规定。
... 如果正确的值超出了可表示值的范围,则 ...
ULONG_MAX
, ... 被返回(根据值的返回类型和符号,如果有的话),宏的值ERANGE
存储在errno
. C17dr § 7.22.1.4 8
因此,当字符串在范围内strtoul()
时会产生一个非错误值。[-ULONG_MAX ... +ULONG_MAX]
转换后的结果是unsigned long
[0 ... ULONG_MAX]
. 错误结果总是ULONG_MAX
.
推荐阅读
- excel - 是否有一个函数或函数组合可以在 Excel 中返回斜率的 SE?
- docker - 在 Docker Compose 中更改 Redis 端口不起作用
- android - React Native:目前如何接受 Android 上的所有许可协议
- reactjs - 使用 React Router 动态渲染组件的问题
- c# - 为什么 C# 插值字符串不接受可变填充?
- .net-core - AppSettings 连接字符串必须可以通过环境(Dev、Staging、UAT 和 Production)进行配置
- python - 如何创建 ModelFormset 来编辑未保存模型的某些字段?
- python - 使 plt.yticks(np.arange(0, 1, step=0.1)) 实际上覆盖 0 到 1 的范围
- java - 在 java 中登录 adfs 并从 saml 响应中获取断言
- android - 如何将使用 Python 代码生成的数组发送到我的 Android 应用程序?