任何类型的指针都可以指向任何东西?

时间:2013-10-31 15:28:42

标签: c++ pointers

这句话是否正确?任何" TYPE"指针可以指向任何其他类型? 因为我相信,仍然有疑虑。

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

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

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

我错了吗?这个陈述是否正确?

5 个答案:

答案 0 :(得分:7)

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

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

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

需要重复:这是特定于平台的

我相信英特尔x86架构会对所有指针都一视同仁 但是你可能会遇到其他并非如此的平台。

答案 1 :(得分:5)

每个指针都属于某种特定类型。有一个特殊的通用指针类型void*可以指向任何东西,但你必须先将void*转换为某种特定的指针类型,然后才能取消引用它。

您可以将指针值从一种指针类型转换为另一种指针类型。在大多数情况下,将指针从foo*转换为bar*并返回foo*将产生原始值 - 但在所有情况下实际上并未得到保证。

可以使foo*类型的指针指向bar类型的对象,但(a)通常是个坏主意,(b)in在某些情况下,它可能无效(例如,如果目标类型foobar具有不同的大小或对齐要求)。

你可以逃避:

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

导致p指向n - 但*p未提供n的值,它会为您提供第一个字节的值n作为char

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

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

(有些语言只有非类型的通用指针。在这种语言中,避免类型错误更加困难,例如存储一种类型的值并意外地访问它,就像它是另一种类型一样。)< / p>

答案 2 :(得分:4)

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

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

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

在程序中处理数据的方式方面,类型也很重要。例如,如果你有一个int存储在内存中,但你可以通过解除引用float*指针来访问它,那么你可能会得到无用的结果(除非你为特定的方式编程了它)原因)。这是因为int存储在内存中的方式与float的存储方式不同。

答案 3 :(得分:1)

  

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

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

可以使用reinterpret_cast将指针从一种类型转换为另一种类型,但除非这些指针可以使用static_cast合法转换,否则reinterpret_cast无效。因此,除非Foo* foo = ...; Bar* bar = (Bar*)foo;Foo实际相关,否则您无法Bar

您还可以使用reinterpret_cast从对象指针转换为void*,反之亦然,从这个意义上说,void*可以指向任何东西 - 但这不是你的意思好像在问。

此外,你可以reinterpret_cast从对象指针到积分值,反之亦然,但同样,不是你想要的那样。

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

另请注意,每次在任何上下文中使用reinterpret_cast时,您都会在悬崖峭壁上跳舞。当类型不相关时,当Foo实际指向的事物是Bar时,取消引用指向{{1}}的指针。你不惜一切代价避免这些类型的演员阵容。

答案 4 :(得分: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

对齐

此外,对齐方面存在问题:根据平台的不同,某些 数据类型可能需要在内存中进行对齐。这在矢量数据类型中尤为常见,但可以应用于任何大于字节的类型。例如,如果int必须对齐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

严格的别名并键入punning

最后,有严格的别名规则:一定不能通过指向另一种类型的指针来访问一种类型的对象。禁止键入punning:

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类型提供了一个例外:您可以将任何指针投射到char*,复制将char数据传递到另一个缓冲区,然后像访问其他类型一样访问该另一个缓冲区。结果由实现定义,但标准允许。用例主要是通用的数据操作例程,例如I / O等。


TL; DR:

指针的互换性比您想象的要少得多。除了到char*之外,不要以其他任何方式重新解释指针(在“ from”情况下检查对齐方式)。甚至对于函数指针也不起作用。