【问题标题】:Why does len() returned a signed value?为什么 len() 返回一个有符号值?
【发布时间】:2023-03-02 21:52:02
【问题描述】:

Go 的内置 len() function 返回一个签名的 int。为什么不使用 uint 代替?

len() 是否有可能返回负面信息?
据我所知,答案是否定的:

  • Arrays: "元素的个数叫做长度,永远不会是负数。"
  • Slices:“在任何时候,以下关系都成立:0 <= len(s) <= cap(s)
  • Maps“地图元素的数量称为它的长度”。 (我在规范中找不到任何明确将其限制为非负值的内容,但我很难理解为什么地图中的元素可能少于 0 个)
  • Strings“字符串值是一个(可能为空的)字节序列......字符串 s 的长度(其大小以字节为单位)可以使用内置函数 len() 来发现”(同样,很难看看一个序列如何有一个负数的字节)
  • Channels "通道缓冲区中排队的元素数量(同上)

【问题讨论】:

  • 如果您想处理切片的第 2147483648 个元素,uint 可能会更好。但是,如果你想处理第 4294967296 个元素,你就会遇到麻烦。

标签: go signed


【解决方案1】:

Length and capacity

内置函数 len 和 cap 接受各种类型的参数和 返回 int 类型的结果。该实施保证了 结果总是适合 int。

Golang 是强类型语言,所以如果 len()uint 则不是:

i := 0 // int
if len(a) == i {
}  

你应该写:

if len(a) == uint(i) {
}

或:

if int(len(a)) == i {
}

另见:

uint 32 位或 64 位
intuint 大小相同
uintptr an unsigned integer large enough to store the uninterpreted bits of a pointer value

也为了与 C 兼容:CGo C.size_t 和 C 中的数组大小为 int 类型。

【讨论】:

  • 是否需要转换?一个快速的实验表明您可以毫无问题地将 intuint 值与整数文字进行比较(尽管尝试比较 intuint variables 会导致“类型不匹配”错误) .
  • @KeithThompson 对于未键入的数字你是对的,这是虚构的例子,谢谢。
【解决方案2】:

来自the spec:

长度是数组类型的一部分;它必须计算为一个非负常数,该常数可由int 类型的值表示。可以使用内置函数len 来发现数组 a 的长度。这些元素可以通过整数索引0len(a)-1 来寻址。数组类型总是一维的,但可以组合成多维类型。

我意识到说规范规定 X 可能有点循环,因为规范规定 Y,但因为长度不能超过 @ 的最大值987654326@,len 不可能返回一个 uint-exclusive 值,因为它返回一个负值。

【讨论】:

  • (推测性)我可以想到一些很好的理由让语言的作者希望数组被 signed 整数索引。数组通常被分配为有凝聚力的内存块[需要引用],指针算法用于寻址元素[需要引用]。您可以将元数据(例如数组的长度)存储在负偏移量中[需要引用],并且通过添加可能为负数或可能不是负数的偏移量来表示某些数组操作在环境上可能更容易,而不是有条件地添加或减去偏移量。
【解决方案3】:

len()(和cap())返回int,因为这是用来索引切片和数组的(不是uint)。所以问题更多的是“当没有负索引时,为什么 Go 使用有符号整数来索引切片/数组?”。

答案很简单:计算索引是很常见的,如果以无符号整数进行此类计算,很容易下溢。对于 ab 的 6 和 10 的无辜值,像 i := a-b+7 这样的无辜代码可能会产生 i == 4294967291。这样的索引可能会溢出您的切片。许多索引计算发生在 0 左右,并且使用无符号整数很难正确计算,这些错误隐藏在数学上完全合理和合理的公式后面。这既不安全也不方便。

这是基于经验的权衡:下溢往往发生在使用无符号整数进行的索引计算中,而如果使用有符号整数进行索引计算,则上溢则不太常见。

另外:在这些情况下使用无符号整数的好处基本上为零。

【讨论】:

  • 我认为 Go 运行时会检查对数组或切片的任何类型的访问,因此当我们在谈论索引。但是,当使用它来计算要分配的数组的 lengthcapacity 时,无符号数学仍然可能存在问题。
  • @RomanKhimov 是的,如果发生上溢/下溢,后果是一样的。我的论点是:如果使用有符号整数,则 ... 流更少,如果使用无符号整数进行索引计算,则 ... 流更多,因为 0 附近的索引比 MaxUint32 周围的索引更常见。
  • 这里的例子有错误吗?我不认为 6 - 10 + 7 下溢,是吗?无论如何,我不确定我是否遵循这个论点。是不是在下溢场景中,明显的负索引错误比可能由大的无符号整数索引引起的更微妙的错误更可取?
  • @Carter 1. 我从未声称 6-10+7 会下溢。这是一个在编译时以任意精度求值的常量表达式。我说如果 a 和 b 未签名, a-b+7 可能会导致意外行为。) 2. 每个上溢或下溢都是一个错误。论点是:0 附近的索引计算比 around MaxUint 更常见。使用 uint 进行索引计算更危险,因为会出现更多错误。
【解决方案4】:

有一个提案正在进行中“issue 31795 Go 2: change len, cap to return untyped int if result is constant

可能包含在 Go 1.14(2010 年第一季度)中

我们应该能够毫无问题地为 lencap 做到这一点 - 确实如此 stdlib 中没有任何内容可以通过修改后的类型对其进行类型检查 检查显示

CL 179184 视为 PoC:这仍处于试验阶段。


作为peterSOnoted below,这已关闭。

Robert Griesemer 解释:

正如您所指出的,使 len 始终无类型的问题是 结果。对于布尔值(以及字符串),大小是已知的,无论如何 一种布尔值(或字符串)。

Russ Coxadded:

我不确定这里的成本是否值得。今天有一个简单的 规则:len(x) 的类型为 int。将类型更改为取决于 x 是什么 将以非正交方式与各种代码更改交互。例如, 在建议的语义下,此代码编译:

const x string = "hello"
func f(uintptr)
...
f(len(x))

但假设随后有人出现并希望能够修改 x 为 测试或类似的东西,所以他们s/const/var/。这通常是相当的 安全,但现在 f(len(x)) 调用无法进行类型检查,它将是 神秘的为什么它会起作用。

此更改似乎可能会增加比删除更多的粗糙边缘。

【讨论】:

猜你喜欢
  • 2020-08-11
  • 2015-07-27
  • 1970-01-01
  • 2015-05-11
  • 1970-01-01
  • 2010-12-09
  • 2012-11-12
  • 1970-01-01
  • 2015-09-01
相关资源
最近更新 更多