long 在内存中表示多长时间?

How long long is represented in memory?

我不是高级 C++ 程序员。但是我已经使用 C++ 很长时间了。所以,我喜欢玩它。最近我在考虑以编程方式最大化变量的方法。所以我尝试 Bitwise Operators 来用 1 填充一个变量。然后是 signedunsigned 问题。我对内存表示的了解不是很好。但是,我最终编写了以下适用于 signedunsigned shortintlong 的代码(尽管 intlong 基本相同)。不幸的是,对于 long long,程序失败了。

那么,long long 的幕后发生了什么?它在内存中是如何表示的?此外,有没有更好的方法可以在 C++ 中实现同样的事情?

#include <bits/stdc++.h>
using namespace std;



template<typename T>
void Maximize(T &val, bool isSigned)
{
    int length = sizeof(T) * 8;
    cout << "\nlength = " << length << "\n";

    // clearing
    for(int i=0; i<length; i++)
    {
        val &= 0 << i;
    }

    if(isSigned)
    {
        length--;
    }

    val = 1 << 0;
    for(int i=1; i<length; i++)
    {
        val |= 1 << i;

        cout << "\ni = " << i << "\nval = " << val << "\n";
    }
}


int main()
{
    long long i;

    Maximize(i, true);

    cout << "\n\nsizeof(i) = " << sizeof(i) << " bytes" << "\n";
    cout << "i = " << i << "\n";

    return 0;
}

正如 harold 评论的那样,解决方案是使用 T(1) << i 而不是 1 << i。另外正如一些程序员提到的,long long表示为连续字节(通常为8个字节),如果它是[=14=,则在MSB处有符号位].

您的代码的基本问题在语句中

val &= 0 << i;

val |= 1 << i;

如果 valint 长。

在第一个表达式中,0 << i(很可能)始终为 0,而不管 i(从技术上讲,它会遇到下面描述的相同未定义行为,但您不太可能遇到问题。)所以根本不需要循环;所有语句都做同样的事情,即将 val 清零。当然,val = 0; 会是一种更简单的写法。

问题 1 << i 是常量文字 1 是一个 int(因为它足够小,可以表示为 int,并且 int 是用于整数常量的最窄的表示形式)。由于 1int,因此 1 << i 也是。如果 i 大于或等于 int 中的值位数,则该表达式具有未定义的行为,因此理论上结果可以是任何值。然而,在实践中,结果很可能与 int 的宽度相同,因此只有低位会受到影响。

当然可以将 1 转换为 T 类型(尽管一般来说,当 T 被签名时,您可能需要谨慎对待极端情况),但是它通过使用 cstdintuintmax_t:[=46 中定义的最大宽度无符号整数类型,更容易将 1 转换为至少与 T 一样宽的无符号类型=]

val |= std::uintmax_t(1) << i;

在现实世界的代码中,常见的假设是最宽的整数类型是 long long:

val |= 1ULL << i;

如果程序从不尝试用扩展整数类型实例化模板,这将工作正常。


当然,这不是求整数类型最大值的方法。正确的解决方案是 #include <limits> 然后使用 std::numeric_limits<T>::max()

的适当特化

C++ 只允许对正(和无符号)整数进行一种表示,对负有符号整数进行三种可能的表示。正整数和无符号整数简单地表示为二进制表示法中的位序列。也可能有填充位,有符号整数有一个符号位,在正整数的情况下必须为 0,因此不能保证表示中有 8*sizeof(T) 个有用的位,即使数字一个字节中的位数已知为 8(理论上,它可能更大)。 [注1]

负符号整数的符号位始终为 1,但值位有三种不同的格式。最常见的是“补码”,其中解释为正数的值位将比数字的实际值正​​好多 2k,其中 k 是值位数。 (这相当于给符号位指定了2-k的权重,这也是为什么叫2s补码的原因。)

另一种选择是“一个补码”,其中值位全部单独反转。这与二进制补码表示恰好不同。

第三个允许的替代方案是“sign-magnitude”,其中值位恰好是负数的绝对值。这种表示经常用于浮点值,但很少用于整数值。

符号大小和补码都有一个缺点,即存在表示“负 0”的位模式。另一方面,补码表示的特点是最负的可表示值的大小比最正的可表示值的大小大一个,结果-xx/-1都可以溢出,导致未定义的行为。


注释

  1. 我相信理论上可以在值位和符号位之间插入填充,但我当然不知道任何具有该功能的实际实现。但是,尝试将 1 移到符号位位置是未定义的行为这一事实使得假设符号位与值位连续是不正确的。

I was thinking about ways to maximize a variable programmatically.

您正在尝试重新发明轮子。 C++ STL 已经具有此功能:std::numeric_limits::max()

// x any kind of numeric type: any integer or any floating point value
x = std::numeric_limits<decltype(x)>::max();

这也更好,因为您不会依赖未定义的行为。