这是 C 的一个历史设计错误,在 C++ 中也重复出现。
它可以追溯到 16 位计算机,错误是决定使用所有 16 位来表示高达 65536 的大小,从而放弃了表示负大小的可能性。
unsigned如果含义是“非负整数”(大小在逻辑上不能为负),这在 se 中不会是错误,但这是语言转换规则的问题。
鉴于语言的转换规则,unsignedC 中的类型并不表示非负数,而是更像是位掩码(数学术语实际上是“环的成员ℤ/n”)。看看为什么要考虑 C 和 C++ 语言
unsigned - unsigned给出一个unsigned结果
signed + unsigned给出和unsigned结果
unsigned如果您阅读为“非负数” ,它们显然都毫无意义。
当然,说对象的大小是ℤ/nring 的成员根本没有任何意义,这就是错误所在。
实际影响:
每次处理对象的大小时都要小心,因为值是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 来确保某些值是正数的尝试通常会被隐式转换规则打败