这句话是否正确?任何" TYPE"指针可以指向任何其他类型? 因为我相信,仍然有疑虑。
为什么为明确类型声明指针?例如。 int
或char
?
我可以得到的一个解释是:如果int
类型指针指向char
数组,那么当指针递增时,指针将从0位置跳到2位置,在中间跳过1个位置(因为int size = 2)。
也许是因为指针只保存值的地址,而不是值本身,即int
或double
。
我错了吗?这个陈述是否正确?
答案 0 :(得分:7)
指针可以可以互换,但不是必须的。
特别是,在某些平台上,某些类型需要与某些字节边界对齐。
因此,虽然char
可能位于内存中的任何位置,但int
可能需要位于4字节边界上。
另一个重要的潜在区别是功能指针 函数的指针可能无法与许多平台上的数据类型指针互换。
需要重复:这是特定于平台的。
我相信英特尔x86架构会对所有指针都一视同仁 但是你可能会遇到其他并非如此的平台。
答案 1 :(得分:5)
每个指针都属于某种特定类型。有一个特殊的通用指针类型void*
可以指向任何东西,但你必须先将void*
转换为某种特定的指针类型,然后才能取消引用它。
您可以将指针值从一种指针类型转换为另一种指针类型。在大多数情况下,将指针从foo*
转换为bar*
并返回foo*
将产生原始值 - 但在所有情况下实际上并未得到保证。
你可以使foo*
类型的指针指向bar
类型的对象,但(a)通常是个坏主意,(b)in在某些情况下,它可能无效(例如,如果目标类型foo
和bar
具有不同的大小或对齐要求)。
你可以逃避:
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:
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等。
指针的互换性比您想象的要少得多。除了到char*
之外,不要以其他任何方式重新解释指针(在“ from”情况下检查对齐方式)。甚至对于函数指针也不起作用。