6

这个说法正确吗?指针的任何“类型”都可以指向任何其他类型吗?因为我这么相信,还是有疑问的。

为什么要为明确的类型声明指针?例如intchar

我能得到的一个解释是:如果一个int类型指针指向一个char数组,那么当指针递增时,指针将从 0 位置跳转到 2 位置,在两者之间跳过 1 个位置(因为 int size=2)。

也许是因为指针只保存一个值的地址,而不是值本身,即intor double

我错了吗?这种说法正确吗?

4

5 回答 5

8

指针可以互换,但不是必须的。

特别是,在某些平台上,某些类型需要与某些字节边界对齐。因此,虽然 achar可能在内存中的任何位置,但 anint可能需要位于 4 字节边界上。

另一个重要的潜在区别是函数指针。
在许多平台上,指向函数的指针可能无法与指向数据类型的指针互换。

值得重复:这是特定于平台的

我相信英特尔 x86 架构对所有指针都一视同仁。
但是您很可能会遇到其他不正确的平台。

于 2013-10-31T15:34:58.757 回答
5

每个指针都是某种特定类型。有一种特殊的通用指针类型void*可以指向任何对象类型,但您必须先将 a 转换void*为某种特定的指针类型,然后才能取消引用它。(我忽略了函数指针类型。)

您可以将指针值从一种指针类型转换为另一种。在大多数情况下,将指针从foo*to转换bar*回 tofoo*将产生原始值——但这实际上并不能保证在所有情况下。

可以使 type 的指针指向 typefoo*的对象bar,但是 (a) 这通常是个坏主意,并且 (b) 在某些情况下,它可能不起作用(例如,如果目标类型foo并且bar具有不同的大小或对齐方式要求)。

你可以摆脱这样的事情:

int n = 42;
char *p = (char*)&n;

这导致p指向n- 但*p没有给你的值n,它给你的第一个字节的值n作为 a char

指针运算的不同行为只是具有不同指针类型的部分原因。它主要是关于类型安全。如果你有一个 type 的指针int*,你可以合理地确定(除非你做了一些不安全的事情)它实际上指向一个int对象。如果您尝试将其视为不同类型的对象,编译器可能会抱怨它。

基本上,我们有不同的指针类型,原因与我们有其他不同的类型的原因相同:因此我们可以在编译器的帮助下跟踪每个对象中存储的值类型。

(有些语言只有无类型的泛型指针。在这种语言中,更难避免类型错误,例如存储一种类型的值并意外访问它,就好像它是另一种类型一样。)

于 2013-10-31T15:35:10.570 回答
4

任何指针都可以引用内存中的任何位置,因此从技术上讲,该语句是正确的。话虽如此,在重新解释指针类型时需要小心。

一个指针基本上有两条信息:一个内存位置,以及它期望在那里找到的类型。内存位置可以是任何东西。它可能是存储对象或值的位置;它可能在一串文本的中间;或者它可能只是一个任意的未初始化内存块。

不过,指针中的类型信息很重要。您问题中的数组和指针算术解释是正确的 - 如果您尝试使用指针迭代内存中的数据,那么类型需要正确,否则您可能无法正确迭代。这是因为不同的类型有不同的大小,并且可能有不同的对齐方式。

就程序中数据的处理方式而言,类型也很重要。例如,如果您有一个int存储在内存中,但您通过取消引用float*指针来访问它,那么您可能会得到无用的结果(除非您出于特定原因以这种方式对其进行了编程)。这是因为 aint在内存中的存储方式与 afloat的存储方式不同。

于 2013-10-31T15:42:17.890 回答
1

指针的任何“类型”都可以指向任何其他类型吗?

一般没有。类型必须相关。

可以使用reinterpret_cast将指针从一种类型转换为另一种类型,但除非这些指针可以使用 a 合法转换,否则static_cast无效reinterpret_castFoo* foo = ...; Bar* bar = (Bar*)foo;因此,除非Foo并且Bar实际上是相关的,否则你不能这样做。

您还可以使用reinterpret_castto 从对象指针转换为 a void*,反之亦然,从这个意义上说, avoid*可以指向任何东西——但这似乎不是您要问的。

此外,您可以reinterpret_cast从对象指针到整数值,反之亦然,但同样,这不是您所要求的。

最后,为char*. 您可以使用任何其他类型的地址初始化char*变量,并对结果指针执行指针数学运算。如果指向的东西实际上不是 a ,您仍然无法通过指针取消引用char,但它可以被转换回实际类型并以这种方式使用。

另请记住,每次reinterpret_cast任何情况下使用时,您都是在悬崖峭壁上跳舞。Foo当它实际指向的东西是a 时,取消引用指向 a 的指针会Bar在类型不相关时产生未定义的行为。你最好不惜一切代价避免这些类型的演员表。

于 2013-10-31T15:38:35.860 回答
1

有些指针比其他指针更平等......

首先,并不是所有的指针都必须是同一个东西。例如,函数指针可能与数据指针非常不同。

旁白:PPC 上的函数指针

在 PPC 平台上,这很明显:一个函数指针实际上是两个指针,因此根本没有办法将函数指针有意义地转换为数据指针或返回。即以下将成立:

int* dataP;
int (*functionP)(int);

assert(sizeof(dataP) == 4);
assert(sizeof(functionP) == 8);
assert(sizeof(dataP) != sizeof(functionP));

//impossible:
//dataP = (int*)functionP;          //would loose information
//functionP = (int (*)(int))dataP;  //part of the resulting pointer would be garbage

结盟

此外,对齐存在问题:根据平台,某些数据类型可能需要在内存中对齐。这在矢量数据类型中尤其常见,但可以应用于任何大于字节的类型。例如,如果 aint必须是 4 字节对齐,则以下代码可能会崩溃:

char a[4];
int* alias = (int*)a;
//int foo = *alias;    //may crash because alias is not aligned properly

如果指针来自malloc()调用,这不是问题,因为这可以保证为所有类型返回充分对齐的指针:

char* a = malloc(sizeof(int));
int* alias = (int*)a;
*alias = 0;    //perfectly legal, the pointer is aligned

严格的别名和类型双关

最后,还有严格的别名规则:您不能通过指向另一种类型的指针访问一种类型的对象。禁止使用双关语:

assert(sizeof(float) == sizeof(uint32_t));
float foo = 42;
//uint32_t bits = *(uint32_t*)&foo;    //type punning is illegal

如果您绝对必须将位模式重新解释为另一种类型,则必须使用memcpy()

assert(sizeof(float) == sizeof(uint32_t));
float foo = 42;
uint32_t bits;
memcpy(&bits, &foo, sizeof(bits));    //bit pattern reinterpretation is legal when copying the data

为了允许memcpy()和朋友实际可实现,C/C++ 语言标准为char类型提供了一个例外:您可以将任何指针强制转换为 a char*,将数据复制char到另一个缓冲区,然后以其他类型访问该另一个缓冲区。结果是实现定义的,但标准允许。用例主要是一般的数据操作例程,如 I/O 等。


TL;博士:

指针的可互换性比您想象的要少得多。除了 to/from 之外,不要以任何其他方式重新解释指针char*(检查“from”情况下的对齐方式)。即使这样也不适用于函数指针。

于 2019-06-18T20:36:25.680 回答