gpt4 book ai didi

c++ - 将 `int*`转换为位集指针时会发生什么

转载 作者:行者123 更新时间:2023-12-02 09:59:22 25 4
gpt4 key购买 nike

我正在做一个简单的测试,以数字的二进制表示形式计算1的数量:

  int x;
while (cin >> x) {
bitset<32> xBitmap = {0};
xBitmap = static_cast<bitset<32>>(x);
std::cout << xBitmap.count() << std::endl;
}
上面的代码创建了正确的结果,但是当我使用一个指向位集的指针时,发生了意外情况:
      bitset<32>* xBitmap = nullptr;
xBitmap = static_cast<bitset<32>*>((void*)&x);
std::cout << xBitmap->count() << std::endl;
此代码创建随机结果,每次使用“count()”都会创建不同的结果。我猜这是内存泄漏吗?但是为什么会导致内存泄漏呢?

最佳答案

您的第一个示例中发生了什么?
您有一个类型为int的变量,并执行了static_castint转换为std::bitset<32>。根据static_cast(link)的规范:

static_cast<new_type>(expression)

  1. If there is an implicit conversion sequence from expression to new_type, or if overload resolution for a direct initialization of an object or reference of type new_type from expression would find at least one viable function, then static_cast<new_type>(expression) returns the imaginary variable Temp initialized as if by new_type Temp(expression);, which may involve implicit conversions, a call to the constructor of new_type or a call to a user-defined conversion operator.

...


考虑以下示例:
#include <iostream>

class A {
public:
A(int x) { std::cout << "A " << x << std::endl; }
};

int main(void) {
int y = 13;
A a = static_cast<A>(y);
}
运行该程序将打印 A 13。这意味着在这种情况下 A a = static_cast<A>(y)A a = A(y)等效。这是因为 y的类型为 int,并且 A的构造函数采用 int
如果我们更改示例,以便 A的构造函数采用 std::string,则程序将不再编译:
#include <iostream>
#include <string>

class A {
public:
A(std::string x) { std::cout << "A " << x << std::endl; }
};

int main(void) {
int y = 13;
A a = static_cast<A>(y);
}
编译器会抱怨无法将 int转换为 A
考虑第三个示例:
#include <iostream>

class A {
public:
A(int x) { std::cout << "A " << x << std::endl; }
};

class B {
public:
B(A a) { std::cout << "B" << std::endl; }
};

int main(void) {
int y = 13;
B b = static_cast<B>(y);
}
此示例编译并打印:
A 13
B
因此,这就是规范称为“隐式转换序列”的内容。尽管没有用于 Bint构造函数,但有用于 BA构造函数,然后又有用于 Aint构造函数。因此 static_cast<B>(y)将解析为 B(A(x))。如果我们将 explicit关键字添加到 A的构造函数中,则该示例将不再编译:
  explicit A(int x) { std::cout << "A " << x << std::endl; }
这是因为构造函数上的 explicit关键字禁止在隐式转换序列中使用该构造函数。
这些示例使我们能够了解调用 static_cast<std::bitset<32>>(x)时发生的情况。 std::bitset<N>类具有一个采用 unsigned long( reference)的构造函数。构造函数未使用 explicit关键字标记,因此它可以参与隐式转换序列。可以将 int隐式转换为 unsigned long。因此 static_cast<std::bitset<32>>(x)解析为 std::bitset<32>((unsigned long)x)),因此它创建了 std::bitset<32>的新实例,并将 x的值传递给构造函数。
这就是第一个示例起作用的原因。
您的第二个示例中发生了什么?
您有一个类型为 int的变量。您创建一个指向该变量的指针( &x),然后将该指针转换为 void指针。然后,您将 static_cast指针 void指向 std::bitset<32>指针。根据 static_cast( link)的规范:
  1. A prvalue of type pointer to void (possibly cv-qualified) can be converted to pointer to any object type.

因此,与第一个示例不同,第二个示例不会创建 std::bitmap<32>的新实例。而是, xBitmap指向 x的内存地址,但是将此内存解释为 std::bitmap<32>。但是,存在一个问题: std::bitmap<32>的内存大小可能不等于 int的内存大小。这是特定于实现的,因此C++标准库的不同实现可能对 std::bitmap<32>具有不同的大小。
在我的系统上,使用GCC附带的C++标准库,以下代码将打印 8:
std::cout << sizeof(std::bitset<32>) << std::endl;
这意味着 std::bitset<32>占用8个字节的内存。虽然32位当然只能用4个字节表示,但看来在我的系统上 std::bitset总是会分配8个字节的倍数(即unsigned long)。例如, sizeof(std::bitset<1>)也是 8sizeof(std::bitset<64>)也是,但是 sizeof(std::bitset<65>)是16, sizeof(std::bitset<128>)也是如此,但是 sizeof(std::bitset<129>)是24,依此类推。
而(在我的系统上), int仅占用四个字节。因此,当我们获取 int的内存但将其解释为 std::bitmap<32>时,我们将从大小仅为4个字节的内存分配中读取8个字节(一个 std::bitmap<32>的大小)。因此,我们将在 int的存储器之后再读取四个字节。该内存中可能有任何东西,因此读取会导致未定义的行为。这就是为什么在调用 count()时获得随机值的原因。它将计算 int中的位数,之后还计算四个字节中的位数。
诸如GCC和Clang之类的现代编译器具有一项称为“地址清理”( ASan)的功能,可以帮助您调试此类内存问题。对于GCC,可以使用 -fsanitize=address标志启用它:
$ g++ -fsanitize=address test.cpp
$ ./a.out
123
=================================================================
==16616==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd1c7c5f80 at pc 0x55b34fd334b3 bp 0x7ffd1c7c5f00 sp 0x7ffd1c7c5ef0
READ of size 8 at 0x7ffd1c7c5f80 thread T0
因此,在这种情况下,地址清理会检测到您的程序试图读取超出分配大小的数据。
因此,关于您的有关内存泄漏的问题部分:这不是内存泄漏,而是缓冲区溢出。内存泄漏是指您分配内存,然后忘记释放内存。

关于c++ - 将 `int*`转换为位集指针时会发生什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63464681/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com