当我打印b
和d
时,它们都拥有相同的地址(a
的地址)。
那么为什么*b
打印0
和*d
打印5?
void main()
{
double a = 5.0;
double *d = &a;
int *b = (int*)d;
int a1 = 10;
cout << "Val of D : " << d << " Address of d :" << &d
<< " Value of *d :" << *d << endl;
cout << "Val of B : " << b << " Address of B :" << &b
<< " Value of *b :" << *b << endl;
}
答案 0 :(得分:7)
让我们对代码进行一些小改动:
#include <iostream>
int main() {
double a = 5.0;
double *d = &a;
char *b = (char *)&a;
int a1 = 10;
for (int i = 0; i < sizeof(double); i++)
std::cout << std::hex << (int)b[i] << "\t";
}
这向我们展示了double
的各个字节,因为它们存储在内存中。我得到的结果是:
0 0 0 0 0 0 14 40
现在,如果你看看它的前四个字节,它们都是零。假设您的int
是两个或四个字节,当您尝试将该内存视为int
时,结果将为零,因为double的所有非零字节都将在以后存储记忆而不是你在int
使用它时所看到的部分。
当然,如果您将其打印为long long int
,则会得到非零结果(要求long long int
至少为64位)。同样,如果您在大端系统上执行此操作,14
的{{1}}和40
字节可能会存储在内存中的第一个字节而不是最后一个字节,所以结果将再次为非零。
底线是,在这种情况下,您的演员表大致相当于double
。它不是取reinterpret_cast
值并将其转换为double
值,而是查看int
占用的内存字节数,并将其解释为他们是double
。
请注意,上面的结果并不是真正需要的,您不能指望它在便携式代码中发生。它 非常普遍并且被广泛期待(例如,在大多数具有IEEE浮点和32位int
s的小端机器上。
答案 1 :(得分:6)
因为double以与int
完全不同的格式表示(即使您假设它们具有相同的大小)。 double
使用floating point format,而int
不使用trap representation。使用double
指针寻址int
是未定义的行为。
相关:the example from the documentation of TransactionScope
。
修改强>
如果您是初学者,int i = 3.14;
之类的内容可能会很奇怪,即double
转换为int
,但是不能使用指向double
的指针表示/转换为int
。这是因为在第一种情况下,编译器负责转换,并自动将3.14
截断为3
,然后将后者表示为int
,即3
}。 double
3
的{{1}}表示在记忆中看起来完全不同。
答案 2 :(得分:0)
此代码由于违反严格别名规则而导致未定义的行为。
由于对齐违规,它也可能导致UB,但我会在这篇文章中假设它没有。
对象d
的类型为double
。但是您尝试通过类型为*b
的glvalue int
来访问其内存。这不是[basic.lval] / 10中列出的允许别名类型之一:
如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:
- 对象的动态类型,
- 对象的动态类型的cv限定版本,
- 与对象的动态类型相似的类型(如4.4中所定义)
- 与对象的动态类型对应的有符号或无符号类型的类型
- 对应于动态类型的cv限定版本的有符号或无符号类型 对象,
- 聚合或联合类型,包括其元素中的上述类型之一或非 静态数据成员(包括递归地,子集合的元素或非静态数据成员) 或包含联盟),
- 一种类型,它是对象动态类型的(可能是cv限定的)基类类型,
- char或unsigned char类型。