C++指针类型转换

4

我来自C#和Java的背景,但似乎无法理解在C++中使用指针进行强制类型转换的含义。

例如:

int x = 1;
char c = *((char*)&x);

它有什么作用?它有什么用处?


4
在C++中,应该避免使用强制类型转换。强制类型转换是一种告诉编译器“不,不,你会认为这是错误的,但我肯定是正确的”的方法。在C++中,让编译器始终正确总是更好的选择。强制类型转换是一种抑制编译器错误的方式。因此,最好针对特定目的询问强制类型转换,而不是一般地进行类型转换。在C++中,应该完全避免您提到的两个示例中的强制类型转换。 - tenfour
1
你提供的两个例子都无法编译,因此它们没有意义。 - jahhaj
1
编辑后代码无法编译:应该是 char c = *((char*)x); - CapelliC
@UnTraDe,你至少需要发布一个可以编译的示例,但即使这样做也并不意味着它是合法的C++。有时在C++中,您拥有指向一种类型的指针,但实际上您知道它是指向另一种类型的指针,因此您需要进行强制转换。 - jahhaj
你在哪里看到使用 *s 进行强制类型转换的代码? - Lexi
显示剩余5条评论
5个回答

7

在你的两个示例中,你犯了一些错误导致代码无法编译。因此我假设你想要做以下操作:

int x = 1;
char c = *((char*)&x);

根据你的体系结构,c 现在将具有x 的最少或最重要字节的值。在这个例子中,这将是0或1(实际上可以用来检测字节顺序)。

你的第二个示例不会起作用,因为你试图忽略 const 导致非法操作/错误转换(这也称为“const正确性”)。

编辑:关于你的“什么意思?”的评论:

在表达式中: &somevariable 将返回somevariable 的地址。 *somevariable 假设somevariable 的内容是实际值的地址,然后返回该地址的内容。

在声明中: datatype 是一个普通的变量/对象。这是按值传递的。 datatype& 是一个引用。这与 Java/C# 中的普通变量完全相同,并通过引用传递。 datatype* 是指针。它只包含实际值所在的地址(参见上文),并且本质上也是通过引用传递的。

实际的强制转换与 Java/C# 非常相似,但指针就是指向实际值的位置。虽然这可能会让你感到困惑,但在 C/C++ 中指针的工作方式与 Java/C# 中使用的标准变量/引用完全相同。

看看这个:

MyClass x; // object of MyClass
MyClass *x; // pointer to an object of MyClass - the actual value is undefined and trying to access it will most likely result in an access violation (due to reading somewhere random).
MyClass *x = 0; // same as above, but now the default value is defined and you're able to detect whether it's been set (accessing it would essentially be a "null reference exception"; but it's actually a null pointer).
MyClass &x = MyClass(); // creating a new reference pointing to an existing object. This would be Java's "MyClass x = new MyClass();"

4
在C++中进行类型转换就像在Java中一样,不需要指针。
int x = 1;
char c = (char) x; // Lose precision

然而,你在这里做的事情是:
int x = 1;
char *c = (char *)x;

告诉编译器 x 的值是一个字符的地址。它等同于

char *c;
c = 1; // Set the address of c to 0x0000000000000001

很少有这种情况需要这样做。


2

在C++中,有两个根本不同的概念,有时都被称为“转换”:一个是转换,另一个是重新解释

转换创建一个新对象,其“值”与现有对象相同,但类型不同。以下是一些示例:

示例1:类型提升

// 1a: promote int to double to get the correct type of division

int numerator = rand(), denominator = rand();
double d = double(numerator) / double(denominator); 

// 1b: convert int to double to achieve a particular argument deduction

int n;
template <typename T> void do_numeric_stuff(T x) { /* ... */ }

do_numeric_stuff(double(n));

示例2:派生到基类的转换

struct B { }; struct D : B { };
D x;

D * p = &x;  // pointer to x
B * q = p;   // implicit conversion; may change the value!

另一方面,重新解释使我们能够将一个变量看作另一个变量。关于这种技术的唯一正确和有用的应用是序列化,在某种形式下。
例3:序列化。
std::ofstream file("output.bin");    // output file
char large_buffer[HUGE];             // in-memory buffer

unsigned int n = get_data();

char const * p = reinterpret_cast<char const *>(&n);
file.write(p, p + sizeof n);                         // write the bytes of `n`
std::copy(p, p + sizeof n, large_buffer);            // ditto

std::copy(large_buffer + 17, large_buffer + 17 + sizeof n,
          reinterpret_cast<char *>(&n));   // repopulate `n` from buffer

标准规定,通过不正确类型的指针(也称“类型转换”)访问对象是未定义的行为。虽然将对象指针存储在 void* 中,然后将其转换回来并使用它是可以的,但不可以将float视为整数等进行操作。唯一可接受的访问一个对象的方式是我所演示的方式,即将类型为T的对象视为数组char[sizeof(T)] —— 也就是说,您可以访问每个对象的底层二进制表示。

1

我曾经很久以前使用过这个习语来访问指定地址上的硬件,在自定义IO板上。例如,要写入PIC(可编程中断控制器)以重置某些标志(虚构代码):

#define PIC_LOC 0x1000
#define PIC_ENABLE_PORT *((char*)(PIC_LOC+0x10))
#define BIT_ENABLE (1 << 3)

...
PIC_ENABLE_PORT |= BIT_ENABLE;
...

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接