许多样式指南(例如Google建议使用int索引数组时作为默认整数使用).随着64位平台的兴起,大多数时候a int只有32位,这不是平台的自然宽度.因此,除了简单的说法,我认为没有理由保持这种选择.我们清楚地看到编译以下代码的位置:
double get(const double* p, int k) {
return p[k];
}
Run Code Online (Sandbox Code Playgroud)
被编译成
movslq %esi, %rsi
vmovsd (%rdi,%rsi,8), %xmm0
ret
Run Code Online (Sandbox Code Playgroud)
其中第一条指令将32位整数提升为64位整数.
如果代码转换成
double get(const double* p, std::ptrdiff_t k) {
return p[k];
}
Run Code Online (Sandbox Code Playgroud)
生成的程序集现在
vmovsd (%rdi,%rsi,8), %xmm0
ret
Run Code Online (Sandbox Code Playgroud)
这清楚地表明,CPU感觉更在家std::ptrdiff_t比使用int.许多C++用户已经迁移到了std::size_t,但我不想使用无符号整数,除非我真的需要模数2^n行为.
在大多数情况下,使用int不会损害性能,因为未定义的行为或有符号整数溢出允许编译器在内部将任何内容int提升为std::ptrdiff_t处理索引的in循环,但我们从上面清楚地看到编译器不会感到宾至如归int.此外,std::ptrdiff_t在64位平台上使用会使溢出不太可能发生,因为当我们看到越来越多的人被int溢出困住时,他们必须处理大于2^31 - 1现在变得非常普遍的整数.
从我所看到的,这使得唯一int脱颖而出似乎是文字,如事实5是int,但我不认为它会引起任何问题,如果我们移动到std::ptrdiff_t一个默认的整数.
我即将成为std::ptrdiff_t我小公司编写的所有代码的事实上的标准整数.这有什么理由可能是一个糟糕的选择吗?
PS:我同意这个名字std::ptrdiff_t是丑陋的事实,这就是为什么我把它改成了il::int_t看起来好看的原因.
PS:据我所知,很多人会建议我使用std::size_t默认整数,我真的想明确表示我不想使用无符号整数作为我的默认整数.std::size_t在STL中使用默认整数是Bjarne Stroustrup和视频交互式面板中的标准委员会所承认的错误:在时间42:38和1:02:50 向我们提问.
PS:在性能方面,任何64位平台,我知道的,+,-和*被编译为通过相同的方式int和std::ptrdiff_t.所以速度没有区别.如果除以编译时常量,速度是相同的.只有当你在64位平台上使用32位整数a/b时才知道这一点时才会划分,b这会给你带来轻微的性能优势.但是这种情况非常罕见,因为我认为不会选择离开std::ptrdiff_t.当我们处理矢量化代码时,这里有一个明显的区别,越小越好,但这是一个不同的故事,没有理由坚持int.在这些情况下,我建议使用固定大小的C++类型.
Rob*_*juk 105
有关C++核心指南的讨论使用了什么:
https://github.com/isocpp/CppCoreGuidelines/pull/1115
Herb Sutter写道,gsl::index将被添加(将来可能std::index),将被定义为ptrdiff_t.
hsutter于2017年12月26日发表评论•
(感谢许多WG21专家对本说明的评论和反馈.)
将以下typedef添加到GSL
namespace gsl { using index = ptrdiff_t; }并推荐
gsl::index所有容器索引/下标/大小.合理
指南建议对下标/索引使用签名类型.见ES.100至ES.107.C++已经使用有符号整数作为数组下标.
我们希望能够教会人们在高警告级别编写简单,自然,无警告的"新清洁现代代码",并且不会让我们写出关于简单代码的"陷阱"脚注.
如果我们没有一个短字可采用类似
index是有竞争力的int和auto,人们仍然会使用int和auto并得到他们的错误.例如,他们会在广泛使用的平台上编写for(int i=0; i<v.size(); ++i)或者for(auto i=0; i<v.size(); ++i)有32位大小的错误,而for(auto i=v.size()-1; i>=0; ++i)这些错误根本不起作用.我认为我们不能for(ptrdiff_t i = ...直面教学,或者人们会接受它.如果我们有一个饱和的算术类型,我们可能会使用它.否则,最好的选择是
ptrdiff_t具有饱和算术无符号类型的几乎所有优点,除了这ptrdiff_t仍然使得普遍的循环样式for(ptrdiff_t i=0; i<v.size(); ++i)在今天的STL容器上发出签名/未签名的不匹配i<v.size()(并且类似地i!=v.size()).(如果未来的STL将其size_type更改为要签名,那么即使最后一个缺点也会消失.)然而,试图教人们经常写作是毫无希望的(也是令人尴尬的)
for (ptrdiff_t i = ... ; ... ; ...).(甚至指南目前只在一个地方使用它,这是一个与索引无关的"坏"示例.)因此,我们应该提供
gsl::index(以后可以考虑作为考虑因素std::index)作为一种类型ptrdiff_t,因此我们希望(而不是尴尬地)教人们定期写作(index i = ... ; ... ; ...).为什么不告诉别人写
ptrdiff_t?因为我们相信告诉别人你在C++中必须做的事情会很尴尬,即使我们做了,人们也不会这样做.写作ptrdiff_t相比,是太丑陋和unadoptableauto和int.添加名称的目的index是使使用正确大小的签名类型尽可能简单和有吸引力.
编辑:Herb Sutter的更多理由
是
ptrdiff_t够大吗?是.标准容器已经被要求不具有可以表示的元素ptrdiff_t,因为减去两个迭代器必须符合difference_type.但是
ptrdiff_t真的够大,如果我有一个内置数组char或byte大于内存地址空间大小的一半,那么有ptrdiff_t多少元素可以表示?是.C++已经使用有符号整数作为数组下标.因此index,请将其用作绝大多数用途的默认选项,包括所有内置数组.(如果遇到极其罕见的数组或类数组类型,大于地址空间的一半及其元素sizeof(1),并且您要小心避免截断问题,请继续使用size_tfor索引这种非常特殊的容器.这种野兽在实践中是非常罕见的,当它们出现时通常不会被用户代码直接索引.例如,它们通常出现在一个内存管理器中,它接管系统分配并包裹个体较小的用户使用的分配,或提供自己的接口的MPEG或类似的分配;在这两种情况下,size_t只应在内存管理器或MPEG类实现内部进行.)
lit*_*die 36
我从一个旧计时器(前C++)的角度来看待它...当天被理解int为该平台的本土词并且可能提供最佳性能.
如果你需要更大的东西,那么你就可以使用它并在性能上付出代价.如果你需要更小的东西(有限的内存,或特定需要固定大小),同样的事情..否则使用int.是的,如果你的值在一个目标平台上的int可以容纳它的范围内,而另一个目标平台上的int不能......那么我们就有了我们编译时特定的特定定义(在它们成为标准化之前我们自己创建).
但是现在,现在,处理器和编译器要复杂得多,而且这些规则并不那么容易应用.更难以预测您选择的性能对未知的未来平台或编译器的影响......我们如何才能真正知道uint64_t在任何特定的未来目标上的表现会比uint32_t更好或更差?除非你是处理器/编译器大师,否则你不......
所以...也许它是老式的,但除非我为Arduino等受限环境编写代码,否则我仍然使用int通用值,我知道在int我编写的应用程序的所有合理目标的大小范围内.并且编译器从那里获取它...这些天通常意味着32位签名.即使假设16位是最小整数大小,它也涵盖了大多数用例..并且大于这个数字的用例很容易识别并用适当的类型处理.
Eya*_* K. 18
大多数程序不会在几个CPU周期的边缘生存和死亡,并且int很容易编写.但是,如果您对性能敏感,我建议使用定义的固定宽度整数类型<cstdint>,例如int32_t或uint64_t.它们的优点在于它们在签名或未签名方面的预期行为,以及它们在内存中的大小.此标题还包括快速变体,例如int_fast32_t,至少是规定的大小,但如果它有助于提高性能,则可能更多.
Upr*_*ted 15
没有正式理由使用int.它不符合标准的任何理智.对于索引,您几乎总是需要签名指针大小的整数.
这就是说,打字int感觉就像你刚刚对Ritchie说的那样,打字std::ptrdiff_t感觉就像Stroustrup只是踢你的屁股.编码人员也是人,不要给他们的生活带来太多的丑陋.我宁愿使用一些容易打字的typedef long或index而不是std::ptrdiff_t.
Dam*_*mon 13
这有点基于意见,但唉,这个问题也有点乞求.
首先,你谈论的是整数和指数,就好像它们是同一个东西,事实并非如此.对于任何类似"整数,不确定大小"这样的东西,简单地使用int当然,大部分时间,仍然是合适的.对于大多数应用程序而言,这在大多数情况下都可以正常工作,编译器对此很满意.默认情况下,没关系.
对于数组索引,这是一个不同的故事.
到目前为止,还有一个正式的正确的事情,那就是std::size_t.在将来,可能有一个std::index_t使得意图在源级别更清晰,但到目前为止还没有.
std::ptrdiff_t作为一个索引"工作",但同样不正确,int因为它允许负指数.
是的,这发生在萨特先生认为正确的事情上,但我不同意.是的,在汇编语言指令级别,这支持很好,但我仍然反对.标准说:
8.3.4/6:
E1[E2]与*((E1)+(E2))[...] 相同由于适用的转换规则+,ifE1是一个数组和E2一个整数,然后E1[E2]引用的是E2-th成员E1.
5.7/5:[...]如果指针操作数和结果都指向同一个数组对象的元素,或者一个超过数组对象的最后一个元素[...],则行为是未定义的.
数组预订是指E2-th成员E1.没有像数组的负数元素这样的东西.但更重要的是,带有负加法表达式的指针算法会调用未定义的行为.
换句话说:任何大小的签名索引都是错误的选择.指数未签名.是的,签署的指数有效,但它们仍然是错误的.
现在,虽然size_t根据定义是正确的选择(一个足够大的无符号整数类型,可以包含任何对象的大小),但对于普通情况或默认情况下它是否是真正的好选择可能是有争议的.
说实话,你最后一次创建一个包含10 19个元素的数组是什么时候?
我个人使用unsigned int默认,因为这允许的40亿个元素对于(几乎)每个应用程序都足够了,并且它已经推动普通用户的计算机非常接近其限制(如果仅仅订阅整数数组,则假设分配了16GB的连续内存).我个人认为默认为64位索引是荒谬的.
如果您正在编写关系数据库或文件系统,那么是的,您将需要 64位索引.但对于普通的"普通"程序,32位索引就足够了,它们只消耗了一半的存储空间.
当保留大量的指数时,如果我能负担得起(因为数组不超过64k元素),我甚至会去uint16_t.不,我不是在开玩笑.
存储真的是这样的问题吗?贪婪地保存两四个字节是荒谬的,不是吗!好吧,不......
大小可能是指针的问题,所以肯定它也可以用于索引.x32 ABI不存在无缘无故.你不会注意到不必要的大索引的开销,如果你总共只有少数几个(就像指针一样,无论如何它们都会在寄存器中,没有人会注意到它们的大小是4还是8字节).
但是想想一个槽映射的例子,你可以在其中存储每个元素的索引(取决于实现,每个元素有两个索引).哎呀,无论你是每次都打L2,还是每次访问都有一个缓存未命中,它确实会让你感到有些不同!更大并不总是更好.
在一天结束时,你必须问问自己你付出了什么,以及你获得了什么回报.考虑到这一点,我的风格建议是:
如果它花费你"没有",因为你只有一个指针和一些指数可以保持,那么只需使用正式的正确(那就是size_t).正式是正确的,正确的总是有效的,它是可读的和可理解的,正确的是...... 从来没有错.
但是,如果它确实花了你(你可能有几百或者几千或一万个指数),你得到的东西是没有价值的(因为你甚至不能存储2 20个元素,所以你是否可以订阅2 32或2 64没有区别),你应该三思而后行太浪费.
jic*_*ick 11
在大多数现代64位体系结构中,int是4个字节,ptrdiff_t是8个字节.如果你的程序使用了大量的整数,使用ptrdiff_t替代int能翻番程序的内存需求.
还要考虑现代CPU经常受到内存性能的瓶颈.使用8字节整数也意味着你的CPU缓存现在拥有的元素数量是以前的一半,所以现在它必须更频繁地等待缓慢的主存储器(这可能很容易需要几百个周期).
在许多情况下,执行"32到64位转换"操作的成本与内存性能完全相形见绌.
所以这是一个int在64位机器上仍然流行的实用原因.
我的建议是不要过多地考虑汇编语言输出,不要过分担心每个变量的确切大小,而不是说"编译器在家里感觉".(我真的不知道你最后一个是什么意思.)
对于花园种类的整数,大多数程序都充满了,平原int应该是一个很好的类型.它应该是机器的自然字大小.它应该是高效的使用,既不浪费不必要的内存,也不会在内存和计算寄存器之间移动时引入大量额外的转换.
现在,确实有很多更专业的用途,普通int的不再合适.特别是,对象的大小,元素的数量和数组的索引几乎总是如此size_t.但这并不意味着所有整数都应该是size_t!
有符号和无符号类型的混合以及不同大小类型的混合也可能导致问题.但是现代编译器很好地处理了大部分问题以及它们为不安全组合发出的警告.因此,只要您使用现代编译器并注意其警告,您就不需要选择不自然的类型,只是为了避免类型不匹配问题.