这是 C 的一个历史设计错误,在 C++ 中也重复出现。
它可以追溯到 16 位计算机,错误是决定使用所有 16 位来表示高达 65536 的大小,从而放弃了表示负大小的可能性。
unsigned
如果含义是“非负整数”(大小在逻辑上不能为负),这在 se 中不会是错误,但这是语言转换规则的问题。
鉴于语言的转换规则,unsigned
C 中的类型并不表示非负数,而是更像是位掩码(数学术语实际上是“环的成员ℤ/n
”)。看看为什么要考虑 C 和 C++ 语言
unsigned - unsigned
给出一个unsigned
结果
signed + unsigned
给出和unsigned
结果
unsigned
如果您阅读为“非负数” ,它们显然都毫无意义。
当然,说对象的大小是ℤ/n
ring 的成员根本没有任何意义,这就是错误所在。
实际影响:
每次处理对象的大小时都要小心,因为值是unsigned
,并且 C/C++ 中的类型有很多对数字不合逻辑的属性。请始终记住,unsigned
这不是指“非负整数”而是“ℤ/n
代数环的成员”,而且最危险的是,在混合运算的情况下,anint
被转换为unsigned int
而不是相反。
例如:
void drawPolyline(const std::vector<P2d>& pts) {
for (int i=0; i<pts.size()-1; i++) {
drawLine(pts[i], pts[i+1]);
}
}
是错误的,因为如果传递一个空的点向量,它将执行非法(UB)操作。原因是它pts.size()
是一个unsigned
.
该语言的规则会将1
(整数)转换为1{mod n}
,将执行减法以ℤ/n
产生(size-1){mod n}
,还将转换i
为{mod n}
表示形式并在 中进行比较ℤ/n
。
C/C++ 实际上定义了一个<
运算符 in ℤ/n
(很少在数学中完成),即使输入向量为空,您最终也会访问pts[0]
, pts[1]
... 等等直到巨大的数字。
一个正确的循环可能是
void drawPolyline(const std::vector<P2d>& pts) {
for (int i=1; i<pts.size(); i++) {
drawLine(pts[i-1], pts[i]);
}
}
但我通常更喜欢
void drawPolyline(const std::vector<P2d>& pts) {
for (int i=0,n=pts.size(); i<n-1; i++) {
drawLine(pts[i], pts[i+1]);
}
}
unsigned
换句话说,尽快摆脱,只使用常规整数。
永远不要unsigned
用来表示容器或计数器的大小,因为unsigned
意味着“成员ℤ/n
”,而容器的大小不是其中之一。无符号类型很有用,但不能表示对象的大小。
不幸的是,标准 C/C++ 库做出了这个错误的选择,现在修复它为时已晚。但是,您不会被迫犯同样的错误。
用 Bjarne Stroustrup 的话来说:
使用 unsigned 而不是 int 来获得更多位来表示正整数几乎不是一个好主意。通过声明变量 unsigned 来确保某些值是正数的尝试通常会被隐式转换规则打败