为什么需要声明明确类型的指针?例如:int或char?
我能想到的一个解释是:如果一个int类型的指针指向一个char数组,那么当指针被增加时,指针将从第0个位置跳到第2个位置,中间跳过第1个位置(因为int大小为2)。
也许是因为指针只保存值的地址,而不是值本身,即int或double。
我错了吗?那个说法正确吗?
指针可能是可互换的,但不一定必须互换。
特别地,在某些平台上,某些类型需要对齐到特定的字节边界。因此,虽然char
可以在内存中的任何位置,但int
可能需要在4字节边界上。
另一个重要的潜在差异是函数指针。
在许多平台上,指向函数的指针可能无法与指向数据类型的指针互换。
需要强调的是:这是特定于平台的。
我相信Intel x86体系结构将所有指针视为相同的。但是您可能会遇到其他平台,该语句不成立。
void*
,它可以指向任何对象类型,但在您可以对其进行解引用之前,必须将 void*
转换为某个特定的指针类型。(我忽略了函数指针类型。)foo*
转换为 bar*
,然后再转换回 foo*
,会产生原始值——但实际上并不保证在所有情况下都是这样。foo*
的指针指向类型为 bar
的对象,但是(a)这通常是一个坏主意,而且(b)在某些情况下,可能行不通(例如,如果目标类型 foo
和 bar
具有不同的大小或对齐要求)。int n = 42;
char *p = (char*)&n;
p
指向n
-- 但是*p
不会给你n
的值,它将会把 n
的第一个字节作为char
类型来输出。int*
类型的指针,你可以相对确信(除非你做了不安全的操作)它实际指向一个int
对象。如果你试图将其作为另一种类型的对象处理,编译器通常会发出警告。char*
可以别名任何其他指针类型。 - Simplechar*
更大,因此不能与普通指针互换。 - cmaster - reinstate monicavoid*
和函数指针之间的转换是否被允许,也存在一些争议。(在我看来,它是可以的,但是它具有未定义的行为。)但至少,将函数指针转换为void*
可能会丢失信息。 - Keith Thompsonint
的地址分配给char*
指针,您的编译器可能会发出错误或警告。您可以使用reinterpret_cast<>()
来覆盖错误/警告。在某些情况下,这是必要的,但绝对不是大多数程序中应该做的事情。 - Peter Bloomfield首先,不是所有的指针都是同一种东西。例如,函数指针可以与数据指针非常不同。
Aside: Function pointers on PPC
On the PPC platform, this was quite obvious: A function pointer was actually two pointers under the hood, so there was simply no way to meaningfully cast a function pointer to a data pointer or back. I.e. the following would hold:
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
最后,有严格的别名规则:您不能通过指向另一种类型的指针访问一个对象。类型转换是被禁止的:
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”情况下检查对齐)外,不要以任何其他方式重新解释指针。即使对于函数指针也是如此。
任何类型的指针都可以指向其他类型吗?
通常情况下不行。类型必须相关。
可以使用 reinterpret_cast
将一个指针从一种类型转换为另一种类型,但除非这些指针可以合法地使用 static_cast
进行转换,否则 reinterpret_cast
是无效的。因此,除非 Foo
和 Bar
实际上是相关的,否则不能执行 Foo* foo = ...; Bar* bar = (Bar*)foo;
。
您还可以使用 reinterpret_cast
将对象指针转换为 void*
,反之亦然,在这个意义上,void*
可以指向任何东西——但这不是您似乎在询问的内容。
此外,您可以从对象指针到整数值进行 reinterpret_cast
,反之亦然,但同样不是您所询问的内容。
最后,对于char*
特别例外。您可以使用任何其他类型的地址初始化char*
变量,并对生成的指针执行指针数学运算。如果被指向的东西实际上不是char
,则仍然无法通过指针进行解引用,但它可以转换回实际类型并以这种方式使用。
还要记住,每当您在任何上下文中使用reinterpret_cast
时,您都在悬崖边跳舞。当类型不相关时,将指针解引用为Foo
,而实际上它指向的是Bar
会产生未定义的行为。您最好尽可能避免使用这些类型的转换。
int
的char*
上进行指针运算,但如果你使用该指针并且它不指向实际上是char
的东西,那么你将得到未定义的行为。 - John Diblingchar*
必须实际指向一个 char
才能起作用。这是最基本的要求。 - John Dibling