sizeof:

为了确定特定机器上数据类型的大小,C++ 提供了一个名为 的运算符sizeof。sizeof运算符是一个一元运算符,它接受一个类型或一个变量作为参数,并返回该类型对象的大小(以字节为单位)。您可以编译并运行以下程序来了解某些数据类型的大小:

#include <iomanip> // for std::setw (which sets the width of the subsequent output)
#include <iostream>
#include <climits> // for CHAR_BIT

int main()
{
    std::cout << "A byte is " << CHAR_BIT << " bits\n\n";

    std::cout << std::left; // left justify output

    std::cout << std::setw(16) << "bool:" << sizeof(bool) << " bytes\n";
    std::cout << std::setw(16) << "char:" << sizeof(char) << " bytes\n";
    std::cout << std::setw(16) << "short:" << sizeof(short) << " bytes\n";
    std::cout << std::setw(16) << "int:" << sizeof(int) << " bytes\n";
    std::cout << std::setw(16) << "long:" << sizeof(long) << " bytes\n";
    std::cout << std::setw(16) << "long long:" << sizeof(long long) << " bytes\n";
    std::cout << std::setw(16) << "float:" << sizeof(float) << " bytes\n";
    std::cout << std::setw(16) << "double:" << sizeof(double) << " bytes\n";
    std::cout << std::setw(16) << "long double:" << sizeof(long double) << " bytes\n";

    return 0;
}

在这里插入图片描述

有符号整数溢出

在这里插入图片描述

#include <iostream>

int main()
{
    // assume 4 byte integers
    int x { 2147483647 }; // the maximum value of a 4-byte signed integer
    std::cout << x << '\n';

    x = x + 1; // integer overflow, undefined behavior
    std::cout << x << '\n';

    return 0;
}

在这里插入图片描述

无符号整数溢出

如果我们尝试将这个数字280(需要 9 位来表示)存储在一个 1 字节(8 位)无符号整数中,会发生什么?答案是溢出。

如果无符号值超出范围,则将其除以比该类型最大数字大一的数字,并且只保留余数。

这个数字280太大,无法放入 0 到 255 的 1 字节范围中。比该类型的最大数字大 1 是 256。因此,我们将 280 除以 256,得到 1 余数 24。24 的余数就是存储的数字。

这是思考同一件事的另一种方式。任何大于该类型可表示的最大数字的数字都会简单地“回绕”(有时称为“模回绕”)。255在 1 字节整数的范围内,因此255没有问题。256但是, 超出了范围,因此它会回绕到值0。257回绕到值1。280回绕到值24。
在这里插入图片描述

#include <iostream>

int main()
{
    unsigned short x{ 65535 }; // largest 16-bit unsigned value possible
    std::cout << "x was: " << x << '\n';

    x = 65536; // 65536 is out of our range, so we get modulo wrap-around
    std::cout << "x is now: " << x << '\n';

    x = 65537; // 65537 is out of our range, so we get modulo wrap-around
    std::cout << "x is now: " << x << '\n';

    return 0;
}

在这里插入图片描述

无符号数的争议

许多开发人员(以及一些大型开发公司,例如 Google)认为开发人员通常应该避免使用无符号整数。

这主要是因为两种行为可能会导致问题。

首先,对于有符号值,需要做一些工作才能意外溢出范围的顶部或底部,因为这些值远离 0。对于无符号数,溢出范围的底部要容易得多,因为范围的底部是 0,这接近我们的大多数值所在的位置。

考虑两个无符号数的减法,例如 2 和 3:

#include <iostream>

// assume int is 4 bytes
int main()
{
	unsigned int x{ 2 };
	unsigned int y{ 3 };

	std::cout << x - y << '\n'; // prints 4294967295 (incorrect!)

	return 0;
}

您和我都知道2 - 3是-1,但-1不能表示为无符号整数,因此我们得到溢出和以下结果:
在这里插入图片描述
另一种常见的不必要的回绕发生在无符号整数反复减 1,直到它试图减到负数时。在引入循环时,你会看到这样的例子。

其次,更隐蔽的是,混合使用有符号整数和无符号整数时,可能会导致意外行为。在 C++ 中,如果一个数学运算(例如算术或比较)涉及一个有符号整数和一个无符号整数,则有符号整数通常会转换为无符号整数。因此,结果将是无符号的。例如:

#include <iostream>

// assume int is 4 bytes
int main()
{
	unsigned int u{ 2 };
	signed int s{ 3 };

	std::cout << u - s << '\n'; // 2 - 3 = 4294967295

	return 0;
}

在这里插入图片描述
在这种情况下,如果u是有符号的,就会产生正确的结果。但由于u是无符号的(这一点很容易被忽略),s所以会被转换为无符号的,结果 ( -1) 会被当作无符号值处理。由于-1不能存储在无符号值中,所以我们会得到溢出和意外的答案。

这是另一个出错的例子:

#include <iostream>

// assume int is 4 bytes
int main()
{
    signed int s { -1 };
    unsigned int u { 1 };

    if (s < u) // -1 is implicitly converted to 4294967295, and 4294967295 < 1 is false
        std::cout << "-1 is less than 1\n";
    else
        std::cout << "1 is less than -1\n"; // this statement executes

    return 0;
}

在这里插入图片描述
这个程序结构良好,可以编译,而且看起来逻辑一致。但它打印出了错误的答案。虽然在这种情况下,编译器应该会警告你符号/无符号不匹配的问题,但对于其他不存在此问题的情况(例如,两个数字都是正数),编译器也会生成相同的警告,这使得很难检测到何时存在实际问题。

此外,还有其他一些难以检测的问题情况。请考虑以下情况:

#include <iostream>

// assume int is 4 bytes
void doSomething(unsigned int x)
{
    // Run some code x times

    std::cout << "x is " << x << '\n';
}

int main()
{
    doSomething(-1);

    return 0;
}

在这里插入图片描述

doSomething() 的作者原本以为调用此函数时只会传入正数。但调用者传入的却是-1——这显然是个错误,但无论如何,这都是一个错误。在这种情况下会发生什么?

有符号的参数-1被隐式转换为无符号参数。-1不在无符号数字的范围内,因此它会环绕到 4294967295。然后你的程序就会崩溃。

更糟糕的是,这种情况很难避免。除非你配置了编译器,让它主动生成有符号/无符号转换警告(你应该这么做),否则你的编译器很可能不会对此发出任何警告。

所有这些问题都是常见的,会产生意外行为,并且很难发现,即使使用旨在检测问题案例的自动化工具也很难发现。

鉴于上述情况,我们提倡的有争议的最佳实践是,除非在特定情况下,否则避免使用无符号类型

更多推荐