Mac*_*ade 10 c integer coding-style
在我自己的代码中使用整数值时,我总是试着考虑签名,问自己整数应该是有符号还是无符号.
当我确定该值永远不需要为负数时,我会使用无符号整数.
我不得不说这种情况大多数时间都会发生.
在阅读其他人的代码时,我很少看到无符号整数,即使代表的值不能为负数.
所以我问自己:"这有充分的理由吗,或者人们只是使用签名整数,因为不关心»?
我在这里和其他地方搜索了这个主题,我不得不说,当它适用时,我找不到使用无符号整数的充分理由.
我遇到了这些问题:«默认int类型:签名或无符号?»和« 你应该总是使用'int'代表C中的数字,即使它们是非负数吗?»这两个都提供以下示例:
for( unsigned int i = foo.Length() - 1; i >= 0; --i ) {}
Run Code Online (Sandbox Code Playgroud)
对我来说,这只是糟糕的设计.当然,它可能会导致无限循环,无符号整数.
但是foo.Length()在循环之前是否很难检查是否为0?
所以我个人认为这不是一直使用有符号整数的好理由.
有些人也可能会说有符号整数可能很有用,即使对于非负值,通常也可以提供错误标记-1.
好吧,拥有一个特定值意味着"错误"是件好事.
但那么,UINT_MAX对于那个具体价值的东西有什么不对?
我实际上是在问这个问题,因为它可能会导致一些巨大的问题,通常是在使用第三方库时.
在这种情况下,您经常需要处理有符号和无符号值.
大多数时候,人们只是不关心签名,只是unsigned int在signed int没有检查范围的情况下将a分配给a .
我不得不说我对编译器警告标志有点偏执,所以在我的设置中,这样的隐式转换将导致编译器错误.
对于那种东西,我通常使用函数或宏来检查范围,然后使用显式转换分配,如果需要则引发错误.
这对我来说似乎合乎逻辑.
作为最后一个例子,我也是Objective-C开发人员(注意这个问题与Objective-C无关):
- ( NSInteger )tableView: ( UITableView * )tableView numberOfRowsInSection: ( NSInteger )section;
Run Code Online (Sandbox Code Playgroud)
对于那些不熟悉Objective-C的人来说,NSInteger是一个有符号整数.
对于特定部分,此方法实际上检索表视图中的行数.
结果永远不会是负值(顺便说一下,作为节号).
那么为什么要使用有符号整数呢?
我真的不明白.
这只是一个例子,但我总是看到那种东西,用C,C++或Objective-C.
所以,我只是想知道人们是否只是不关心那种问题,或者是否最终有一个好的和有效的理由不在这种情况下使用无符号整数.
期待听到您的答案:)
一个signed返回值可能会产生更多的信息,(认为错误号码,0有时是一个有效的答案,-1显示错误,请参阅man read)...这可能是相关的特别是对图书馆的发展.
如果你担心在使用时获得的额外一点unsigned而不是signed那么你可能会使用错误的类型.(也有点"过早优化"的说法)
python,ruby,jscript等语言在没有signedvs的情况下做得很好unsigned.这可能是一个指标......
\n\n当在我自己的代码中使用整数值时,我总是尝试考虑符号性,问自己整数是否应该有符号或无符号。
\n当我确定该值永远不需要为负数时,我会使用无符号整数。\n我不得不说这种情况大多数时候都会发生。
\n
每次声明变量时仔细考虑哪种类型最合适是非常好的做法!这意味着你很细心而且很专业。您不仅应该考虑符号性,还应该考虑您期望该类型具有的潜在最大值。
\n当不需要时不应该使用带符号类型的原因与性能无关,而是与类型安全有关。有符号类型可能会导致许多潜在的、微妙的错误:
\nC 中存在的各种形式的隐式提升可能会导致您的类型以意外且可能危险的方式改变符号性。整数提升规则是通常算术转换的一部分,赋值时的左值转换,例如 VA 列表使用的默认参数提升,等等。
\n当使用任何形式的按位运算符或类似的硬件相关编程时,有符号类型都是危险的,并且很容易导致各种形式的未定义行为。
\n通过声明你的整数无符号,你会自动跳过上面的很多危险。类似地,通过将它们声明为一样大unsigned int或更大,您可以摆脱整数提升带来的许多危险。
在编写坚固、可移植和安全的代码时,大小和符号都很重要。这就是为什么您应该始终使用来自stdint.hC 的类型而不是本机的所谓“原始数据类型”的原因。
\n\n所以我问自己:\xc2\xabis 有一个很好的理由,还是人们只是使用有符号整数,因为不关心\xc2\xbb?
\n
我真的不认为这是因为他们不在乎,也不是因为他们懒惰,尽管声明一切int有时被称为“草率打字”——这意味着草率地选择类型,而不是意味着懒得打字。
我宁愿相信这是因为他们对我上面提到的各种事情缺乏更深入的了解。有大量经验丰富的 C 程序员不知道隐式类型提升在 C 中如何工作,也不知道带符号类型与某些运算符一起使用时如何导致定义不明确的行为。
\n这实际上是微妙错误的一个非常常见的来源。许多程序员发现自己盯着编译器警告或特殊错误,他们可以通过添加强制转换来消除这些警告。但他们不明白为什么,他们只是简单地添加了演员阵容并继续前进。
\n\n\nfor( 无符号 int i = foo.Length() - 1; i >= 0; --i ) {}
\n对我来说,这只是糟糕的设计
\n
它的确是。
\n曾几何时,向下计数循环会产生更有效的代码,因为编译器选择添加“如果为零则分支”指令而不是“如果更大/更小/等于则分支”指令 - 前者更快。但当时编译器真的很愚蠢,我不相信这种微优化不再相关。
\n因此几乎没有理由进行递减计数循环。无论谁提出这个论点,都可能无法跳出框框思考。该示例可以重写为:
\nfor(unsigned int i=0; i<foo.Length(); i++)\n{\n unsigned int index = foo.Length() - i - 1;\n thing[index] = something;\n}\nRun Code Online (Sandbox Code Playgroud)\n这段代码不应该对性能产生任何影响,但是循环本身变得更容易阅读,同时修复了示例中存在的错误。
\n就当今的性能而言,人们可能应该花时间思考哪种形式的数据访问在数据缓存使用方面是最理想的,而不是其他任何事情。
\n\n\n有些人可能还会说,即使对于非负值,有符号整数也可能有助于提供错误标志(通常为 -1)。
\n
这是一个糟糕的论点。良好的 API 设计使用专用的错误类型来报告错误,例如枚举。
\n而不是拥有一些业余爱好者级别的 API,例如
\nint do_stuff (int a, int b); // returns -1 if a or b were invalid, otherwise the result\nRun Code Online (Sandbox Code Playgroud)\n你应该有类似的东西:
\nerr_t do_stuff (int32_t a, int32_t b, int32_t* result);\n\n// returns ERR_A is a is invalid, ERR_B if b is invalid, ERR_XXX if... and so on\n// the result is stored in [result], which is allocated by the caller\n// upon errors the contents of [result] remain untouched\nRun Code Online (Sandbox Code Playgroud)\n然后,API 将始终保留此错误类型的每个函数的返回值。
\n(是的,许多标准库函数滥用返回类型来进行错误处理。这是因为它包含许多在发明良好编程实践之前的古老函数,并且出于向后兼容性的原因,它们已按原样保留。所以仅仅因为你在标准库中发现了一个写得不好的函数,你就不应该自己跑去写一个同样糟糕的函数。)
\n总的来说,听起来你知道自己在做什么,并且对签名进行了一些思考。这可能意味着在知识方面,您实际上已经领先于撰写您所指的这些帖子和指南的人。
\n例如,谷歌的风格指南就值得怀疑。对于许多其他使用“权威证明”的编码标准也可以说类似的情况。仅仅因为上面写着Google、NASA或者Linux内核,人们就会盲目地吞下它们,不管实际内容的质量如何。这些标准中有好的东西,但也包含主观意见、猜测或明显的错误。
\n相反,我建议参考真正的专业编码标准,例如MISRA-C。它强制要求对签名、类型提升和类型大小等问题进行大量思考和关注,而不太详细/不太严肃的文档会跳过它。
\n还有CERT C,它不像 MISRA 那样详细和仔细,但至少是一个健全的、专业的文档(并且更专注于桌面/托管开发)。
\n| 归档时间: |
|
| 查看次数: |
1036 次 |
| 最近记录: |