问题

左移40位为什么不能写成1<<40ll?

回答
左移40位为什么不能直接写成 `1 << 40ll`?这个问题涉及到 C++ 中整数类型的大小以及位移操作的规则。要彻底弄明白,咱们得一步步来分析。

核心问题:整数类型的“寿命”

想象一下,计算机里的数字,尤其是整数,是有“大小”限制的。就像一个容器,只能装下特定数量的“东西”。在 C++ 里,我们用不同的数据类型来表示不同大小的数字,比如 `int`、`long`、`long long` 等。

`1` 这个数字,在 C++ 里,默认情况下会被编译器当成一个 `int` 类型来处理。这是一个很小的整数类型,通常是32位。

现在我们来看看 `1 << 40` 这个操作。

`1`: 如上所述,它是个 `int`。
`<<`: 这是左移运算符。它把左边的操作数的所有位向左移动指定的位数。
`40`: 这是我们希望移动的位数。

当编译器看到 `1 << 40` 时,它首先会处理左边的操作数 `1`。因为 `1` 是个 `int`,所以它会认为我们是在对一个 `int` 进行左移。

问题出在哪里?—— 溢出与未定义行为

一个典型的 `int` 是32位的。左移 `40` 位意味着什么?

如果我们有一个32位的 `int`,并且我们试图将其向左移动超过31位(对于有符号整数)或者超过32位(对于无符号整数),就会发生一个叫做“溢出”的事情。更糟糕的是,根据 C++ 标准,当一个左移操作的位数大于或等于被左移类型的位数时,结果是未定义的(Undefined Behavior)。

未定义行为 是 C++ 中最令人头疼的概念之一。它意味着编译器可以做任何事情:

1. 产生看似正确但实际上错误的数字。
2. 程序崩溃。
3. 什么也不做,但后续的代码行为变得不可预测。
4. 甚至可能在某些情况下执行你的代码,但这种行为无法保证在任何地方都一致。

所以,`1 << 40` 这个操作,因为 `1` 是 `int`,而 `int` 的位数(通常是32位)小于我们要移动的位数 `40`,这直接触碰了 C++ 未定义行为的红线。编译器很有可能在编译时就给你报错,或者在你运行时得到一个完全错误的结果,而不是你期望的 `2^40`。

为什么加上 `ll` 就能行?—— 明确告诉编译器“我要用大的容器”

现在我们来看看 `1 << 40ll`。这里的关键在于末尾的 `ll`。

`ll`: 这是 `long long` 后缀。当你在一个整型字面量(比如 `1`)后面加上 `ll` 或 `LL`,你就告诉编译器:“嘿,我这个 `1` 不是普通的 `int`,我要用 `long long` 类型来表示它。”

`long long` 类型在 C++ 标准中被保证至少是64位的。这意味着,你有一个64位的“容器”来装你的数字。

现在我们再来看 `1 << 40ll`:

`1ll`: 编译器会知道这是一个 `long long` 类型的 `1`。
`<< 40`: 当左移操作的左边是 `long long` 类型时,即使右边的 `40` 仍然是 `int`(因为没有后缀),根据 C++ 的“整数提升”规则,右边的操作数 `40` 也会被提升到与左边操作数(即 `long long`)相同的类型,或者至少不会导致类型不匹配的问题。

最关键的是,我们的 `long long` 是64位的。左移 `40` 位,这在64位类型里是完全合法的操作,不会发生上面提到的位溢出导致未定义行为的情况。结果会是 `1` 向左移动 `40` 位,产生一个非常大的数字,这个数字可以被 `long long` 类型完全存储。

总结一下:

1. 类型决定了“容器大小”: `1` 默认是 `int`,而 `int` 的位数通常不够容纳左移40位后的结果,而且也触碰了位移位数大于等于类型位数的规则,导致未定义行为。
2. `ll` 是一个“扩容券”: `1ll` 告诉编译器,`1` 要用 `long long` 这个更大的容器来表示。
3. `long long` 有足够的“空间”: `long long`(至少64位)比我们要移动的位数 `40` 要大,所以 `1ll << 40` 是一个合法且有定义的操作,能够得到预期的 `2^40` 的值。

所以,如果你需要进行可能产生大数值的位移操作,一定要确保参与运算的字面量或者变量是足够大的整数类型(如 `long long`),并且要用相应的后缀 (`ll`) 来明确告诉编译器你的意图,避免掉进未定义行为的坑里。这就像你要搬很多东西,不能只拿一个小箱子,得用一个大卡车才行。而 `ll` 就是你选“大卡车”的凭证。

网友意见

user avatar

踩过这个坑,avr-gcc的sfr_defs.h里, _BV宏的定义就是#define _BV(bit) (1 << (bit))

结果某次发现当bit > 15时, _BV宏的结果永远是0还是0x8000来着,忘了。

原因就是在avr-gcc里整数常量的类型默认是16位int,左移位数超过15时就是行为未定义了。

改成#define _BV(bit) (1UL << (bit))后就一切正常了。(不过没试过超过31的情况。)

题目中的情况,显然应该写成(1ULL << 40)才对吧。

ps. 把gcc-avr和avr-libc升级到最新版,发现sfr_defs.h还是原样。大概他们不认为_BV宏会给用户自己调用吧。。。

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有