为了账号安全,请及时绑定邮箱和手机立即绑定

为什么size_t是无符号的?

/ 猿问

为什么size_t是无符号的?

C++
慕盖茨9453107 2019-11-14 10:53:04

Bjarne Stroustrup用C ++编程语言写道:


无符号整数类型是将存储视为位数组的理想选择。使用无符号而不是整数来获得更多的比特来表示正整数几乎不是一个好主意。通过声明无符号变量来确保某些值是正值的尝试通常会被隐式转换规则所破坏。


size_t似乎是无符号的,“可以再获得一位代表正整数”。那么这是一个错误(或折衷方案)吗?如果是这样,我们是否应该在自己的代码中尽量减少使用它?


Scott Meyers的另一篇相关文章在这里。总之,他建议不要使用unsigned in接口,无论该值是否始终为正。换句话说,即使负值没有意义,也不必使用无符号。


查看完整描述

3 回答

?
MMTTMM

size_t 由于历史原因未签名。


在具有16位指针的体系结构(例如“小型”模型DOS编程)上,将字符串限制为32 KB是不切实际的。


因此,C标准要求(通过要求的范围)ptrdiff_t,带符号的对应项size_t和指针差的结果类型必须有效为17位。


这些原因仍然可以在部分嵌入式编程世界中应用。


但是,它们不适用于现代的32位或64位编程,其中更重要的考虑是,当C和C ++用于数字时,不幸的C和C ++隐式转换规则会将无符号类型变成错误吸引子。因此,算术运算和幅度比较)。有了20到20个事后的眼光,我们现在可以看到采用那些特殊转换规则string( "Hi" ).length() < -3(实际上得到保证)的决定是很愚蠢和不切实际的。但是,该决定意味着在现代编程中,对数字采用无符号类型具有严重的劣势而没有优势-除了满足那些发现unsigned自己是自描述类型名称并且没有想到的人的感觉之外typedef int MyType。


总结起来,这不是一个错误。这是出于当时非常合理,实用的编程原因的决定。它与将期望值从Pascal这样的受边界检查的语言转移到C ++无关(这是一个谬误,但非常普遍,即使有些人从未听说过Pascal)。


查看完整回答
反对 回复 2019-11-14
?
慕沐林林

size_t是unsigned因为负数大小没有意义。


(摘自评论:)


声明是什么,而不是要确保。您什么时候最后一次看到-1号清单?遵循该逻辑太远,您会发现unsigned根本不应该存在,也不应该允许位操作。– 怪胎


更重要的是:出于您应该考虑的原因,地址未签名。大小是通过比较地址生成的;将地址视为带符号的地址会做很多错误的事情,使用带符号的值作为结果将丢失数据,而您对Stroustrup报价的阅读显然认为可以接受,但实际上不可接受。也许您可以解释一个否定地址应该怎么做。– 怪胎


查看完整回答
反对 回复 2019-11-14
?
一只名叫tom的猫

使索引类型无符号的原因是与C对称,并且C ++对半开间隔的偏好。而且,如果您的索引类型将是无符号的,那么也将您的大小类型也设为无符号是很方便的。


在C语言中,可以有一个指向数组的指针。一个有效的指针可以指向数组的任何元素或数组末尾的一个元素。它不能指向数组开始之前的一个元素。


int a[2] = { 0, 1 };

int * p = a;  // OK

++p;  // OK, points to the second element

++p;  // Still OK, but you cannot dereference this one.

++p;  // Nope, now you've gone too far.

p = a;

--p;  // oops!  not allowed

C ++同意并将这一思想扩展到迭代器。


反对无符号索引类型的参数通常会给出一个从后到前遍历数组的示例,并且代码通常如下所示:


// WARNING:  Possibly dangerous code.

int a[size] = ...;

for (index_type i = size - 1; i >= 0; --i) { ... }

该代码仅在带index_type符号的情况下才有效,该符号用作应对索引类型进行符号化(并通过扩展对大小进行符号化)的参数。


该说法没有说服力,因为该代码是非惯用语。观察如果尝试使用指针而不是索引重写此循环,会发生什么情况:


// WARNING:  Bad code.

int a[size] = ...;

for (int * p = a + size - 1; p >= a; --p) { ... }

kes,现在我们有未定义的行为!忽略问题,当它size为0时,我们在迭代结束时遇到了问题,因为我们生成了一个指向第一个元素之前的元素的无效指针。即使我们从不尝试取消引用该指针,这也是未定义的行为。


因此,您可以争论通过更改语言标准来解决此问题,以使其合法地拥有一个指向第一个元素之前的元素的指针,但这不太可能发生。半开间隔是这些语言的基本组成部分,因此让我们编写更好的代码。


正确的基于指针的解决方案是:


int a[size] = ...;

for (int * p = a + size; p != a; ) {

  --p;

  ...

}

许多人发现这很令人不安,因为递减现在位于循环的主体中,而不是位于标头中,但是当for语法主要设计用于半开间隔的正向循环时,就会发生这种情况。(反向迭代器通过推迟递减来解决此不对称性。)


现在,以此类推,基于索引的解决方案变为:


int a[size] = ...;

for (index_type i = size; i != 0; ) {

  --i;

  ...

}

无论带index_type符号的还是无符号的,这都行得通,但是无符号的选择会产生更直接映射到惯用指针和迭代器版本的代码。无符号还意味着,与指针和迭代器一样,我们将能够访问序列的每个元素-为了表示无意义的值,我们不会放弃可能范围的一半。尽管在64位环境中这不是实际问题,但在16位嵌入式处理器中或在构建抽象容器类型以处理范围广泛的稀疏数据时仍然是一个非常现实的问题,仍然可以提供与API相同的API。本机容器。


查看完整回答
反对 回复 2019-11-14

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信