【发布时间】:2018-02-22 01:45:06
【问题描述】:
在 Cppcheck 抱怨 "%u" 是错误的格式说明符以扫描到 int 变量后,我将格式更改为 "%d",但是在提交更改之前再次查看它时,我认为目的可能是防止负面输入。我写了两个小程序来看看区别:
说明符 %d
#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
const char* s = "-4";
int value = -1;
int res = sscanf(s, "%d", &value);
cout << "value:" << value << endl;
cout << "res:" << res << endl;
return 0;
}
说明符 %u
#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
const char* s = "-4";
int value = -1;
int res = sscanf(s, "%u", &value);
cout << "value:" << value << endl;
cout << "res:" << res << endl;
return 0;
}
结果
令人惊讶的是,两个转换说明符都接受了这个符号:
value:-4
res:1
我查看了 cppreference.com 上的文档。对于 C (scanf, fscanf, sscanf, scanf_s, fscanf_s, sscanf_s - cppreference.com) 和 C++ (std::scanf, std::fscanf, std::sscanf - cppreference.com),"%u" 转换说明符的描述是相同的(强调我的):
匹配一个无符号十进制整数。
数字的格式与 strtoul() 所期望的格式相同,base 参数的值为 10。
观察到的行为标准是否合规?我在哪里可以找到此文档?
[更新] 未定义的行为,真的,为什么?
我读到它是simply UB,好吧,为了增加混乱,这里是声明值的版本unsignedhttps://ideone.com/nNBkqN - 我认为-1的分配仍然符合预期,但是“% u" 显然仍然匹配符号:
#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
const char* s = "-4";
unsigned value = -1;
cout << "value before:" << value << endl;
int res = sscanf(s, "%u", &value);
cout << "value after:" << value << endl;
cout << "res:" << res << endl;
return 0;
}
结果:
value before:4294967295
value after:4294967292
res:1
【问题讨论】:
-
你有理由使用
sscanf来“解析”字符串吗?为什么不简单地将其放入std::istringstream并使用>>运算符进行正常的流提取?如果您确定字符串只能包含有效数字,那么也许使用std::stoi? -
如果你使用
%u和unsigned int,你会得到预期的输出:ideone.com/XUaZmV -
关于文档,您链接到的参考资料(或其C++ counterpart)非常好。否则你也可以去official homepage of the C++ standards commite阅读the latest draft。
-
@mch 我认为
sscanf也有机会阻止该标志。在我看来,结果不是预期的。 -
en.cppreference.com/w/cpp/io/c/fscanf 表示
%u期望像strtoul这样的十进制数。 en.cppreference.com/w/cpp/string/byte/strtoul 说“(可选)加号或减号”和“如果减号是输入序列的一部分,则从数字序列计算的数值被否定,就像结果类型中的一元减号一样,这适用于无符号整数环绕规则。”
标签: c++ scanf sign cppcheck conversion-specifier