下面的代码会生成编译器警告:
private void test()
{
byte buffer[100];
for (int i = 0; i < sizeof(buffer); ++i)
{
buffer[i] = 0;
}
}
Run Code Online (Sandbox Code Playgroud)
警告:有符号和无符号整数表达式之间的比较 [-Wsign-compare]
这是因为 sizeof() 返回一个无符号的 size_t。
我已经看到了许多关于如何处理这个问题的建议,但没有一个得到了压倒性的支持,也没有一个有任何令人信服的逻辑,也没有任何参考文献来支持一种明显“更好”的方法。最常见的建议似乎是:
有没有“正确”的方法来解决这个问题?
-- 开始编辑 --
当然,我给出的示例很简单,只是为了演示在索引情况下可能发生的类型不匹配警告。
#3不一定是明显正确的答案,因为 size_t 在递减循环中存在特殊风险,例如
for (size_t i = myArray.size; i > 0; --i)
(数组的大小有一天可能为零)。
#4建议通过适当且必要的检查来处理 size_t 索引的递减,以避免递减超过零。由于这使得代码更难阅读,所以有一些可爱的快捷方式并不是特别可读,因此我将它们称为“技巧”。
#7建议使用不可通用的库,因为它们可能不适用于每个设置。
#8建议保持检查的可读性,但将它们隐藏在非成员方法中,有时称为“自由函数”。
#9建议使用算法而不是循环。作为 size_t 索引问题的解决方案,它被多次提供,并且得到了很多支持。尽管我无法在大多数环境中使用 stl 库并且必须自己编写代码,但我还是将其包含在内。
-- 编辑结束--
我希望获得基于证据的指导或参考,以了解处理此类问题的最佳实践。是否有“标准文本”或风格指南可以解决该问题?已被大型科技公司内部采用/认可的明确方法?新语言版本中即将推出可模拟的解决方案?如果有必要,我会对由一位得到广泛认可的专家提出的不受支持的公开建议感到满意。
所提供的选项似乎都不太有吸引力。这些警告掩盖了我想看到的其他事情。我不想错过可能重要的地方的有符号/无符号比较。通过比较 >=0递减类型的循环变量size_t会导致无符号整数环绕的无限循环,即使我们使用类似 的内容来防止这种情况for (size_t i = sizeof(buffer); i-->0 ;),与 size_t 变量递增/递减/比较也会出现其他问题。size_t - 1当 size_t 意外为零时(例如),测试将产生一个大的正“哎呀”数字strlen(myEmptyString)。将无符号 size_t 转换为整数是一个容器大小问题(不能保证值),当然 size_t 可能比 int 更大。
鉴于我的数组的已知大小远低于 Int_Max,在我看来,将 size_t 转换为有符号整数是最好的,但这让我有点畏缩。特别是如果必须如此的话static_cast<int>。如果它隐藏在函数调用中并进行一些大小测试,则更容易采取,但仍然......
或者也许有一种方法可以关闭警告,但仅用于循环比较?
最合适的解决方案完全取决于具体情况。在您问题中的代码片段的上下文中,最合适的操作可能是达成类型协议- 项目符号列表中的第三个选项。这在这种情况下是合适的,因为整个代码中 的使用i只是为了索引数组 - 在这种情况下,使用int是不合适的 - 或者至少是不必要的。
另一方面,如果i算术对象涉及某个本身有符号的算术表达式,则int可能是合适的,并且强制转换也将按顺序进行。
我建议作为指导方针,涉及最少数量的必要类型转换(显式或隐式)的解决方案是合适的,或者以另一种方式看待它,即最大可能的类型协议。不存在一条“权威”规则,因为所涉及变量的目的和用法在语义上而不是在语法上依赖。在这种情况下,也正如其他答案中指出的那样,支持迭代的新语言功能可以完全避免这个特定问题。
讨论您所说的具体建议:
- 忽略警告
这从来都不是一个好主意 - 有些可能是真正的语义错误或维护问题,当您忽略了数百个警告时,您将如何发现并发出一个警告?
- 关闭警告
一个更糟糕的想法;编译器正在帮助您提高代码质量和可靠性。你为什么要禁用它?
- 使用 size_t 类型的循环变量
在这个精确的例子中,这正是你应该这样做的原因;确切的类型协议应该始终是目标。
- 使用 size_t 类型的循环变量以及避免递减超过零的技巧
该建议与给出的简单示例无关。此外,我认为顾问所说的“技巧”实际上意味着检查或只是纠正代码。不需要“技巧”,而且这个术语完全含糊不清——谁知道顾问的意思是什么?当不需要任何具有此类属性的解决方案时,它暗示了一些非常规且有点“肮脏”的东西。
- 将 size_of(buffer) 转换为 int
如果使用保证在代码的其他地方i使用正确的语义,这可能是必要的。int问题中的示例没有,因此在这种情况下这不是合适的解决方案。本质上,如果在此处导致i其他地方出现类型协议警告,而这些警告本身无法通过表达式中所有操作数的通用size_t类型协议来解决,则强制转换可能是合适的。目标应该是实现零警告和最少的类型转换。
- 一些我没有耐心遵循的极其复杂的建议,通常涉及向量和/或迭代器
如果您不准备详细说明或什至考虑此类建议,那么您最好从问题中省略“建议”。在任何情况下,STL 容器的使用并不总是适合大部分嵌入式目标,过多的代码大小增加和不确定的堆管理是在许多平台和应用程序上避免的原因。
- 我无法在嵌入式环境中加载的库。
并非所有嵌入式环境都具有相同的约束。该限制是针对您的嵌入式环境,而不是针对所有嵌入式环境。然而,解决或避免类型协议问题的“加载库”似乎是一个解决问题的大锤。
- 返回表示 T 字节数的有效 int 或 long 的自由函数
目前尚不清楚这意味着什么。什么是“自由功能”?这只是一个非成员函数吗?这样的函数内部必然有类型情况,那么除了隐藏类型转换之外,您还实现了什么?
- 不要使用循环(一定喜欢这个建议)。
我怀疑您是否需要将该建议包含在您的列表中。无论如何,问题并不局限于循环;出现警告并不是因为您使用了循环,而是因为您使用了<不匹配的类型。