指针转换被认为是昂贵的吗?(例如,转换指针/地址需要多少 CPU 周期),尤其是当您必须非常频繁地执行此操作时,例如(只是显示频率范围的示例,我知道对于这种特殊情况有更好的方法) :
unsigned long long *x;
/* fill data to x*/
for (int i = 0; i < 1000*1000*1000; i++)
{
A[i]=foo((unsigned char*)x+i);
};
(例如,转换指针/地址需要多少 CPU 周期)
在大多数机器代码语言中,只有一种“类型”的指针,因此在它们之间进行转换不需要任何成本。请记住,C++ 类型实际上只存在于编译时。
真正的问题是这种代码会破坏严格的别名规则。您可以在其他地方阅读更多有关此内容的信息,但基本上编译器将通过未定义的行为产生不正确的代码,或者被迫做出保守的假设,从而产生更慢的代码。(请注意,char*
和朋友在某种程度上不受未定义行为部分的约束)
优化器通常必须在存在指针的情况下对变量做出保守的假设。例如,知道变量 x 的值为 5 的常量传播过程将无法在分配给另一个变量(例如,*y = 10)后继续使用此信息,因为 *y 可能是别名x。这可能是在像 y = &x 这样的赋值之后的情况。
作为对 *y 赋值的结果,x 的值也会改变,因此将 x 为 5 的信息传播到 *y = 10 之后的语句可能是错误的(如果 *y 确实是 x 的别名)。但是,如果我们有关于指针的信息,常量传播过程可以进行如下查询:x 可以是 *y 的别名吗?然后,如果答案是否定的,则 x = 5 可以安全地传播。受别名影响的另一个优化是代码重新排序。如果编译器确定 x 不是 *y 的别名,则可以在赋值 *y = 10 之前移动使用或更改 x 值的代码,如果这将改进调度或允许执行更多循环优化。
为了以可预测的方式启用此类优化,C 编程语言的 ISO 标准(包括其较新的 C99 版本,请参阅第 6.5 节第 7 节)规定,不同类型的指针引用相同的指针是非法的(有一些例外)内存位置。这条规则被称为“严格别名”,有时可以显着提高性能,[1] 但众所周知,它会破坏一些其他有效的代码。一些软件项目故意违反了 C99 标准的这一部分。例如,Python 2.x 这样做是为了实现引用计数,[2] 并且需要对 Python 3 中的基本对象结构进行更改以启用此优化。Linux 内核这样做是因为严格的别名会导致内联代码的优化出现问题。 [3] 在这种情况下,当使用 gcc 编译时,调用选项 -fno-strict-aliasing 以防止可能产生意外代码的不需要的优化。[编辑]
http://en.wikipedia.org/wiki/Aliasing_(computing)#Conflicts_with_optimization
在您可能遇到的任何架构上,所有指针类型都具有相同的表示形式,因此表示相同地址的不同指针类型之间的转换没有运行时成本。这适用于 C 中的所有指针转换。
在 C++ 中,一些指针转换是有代价的,而另一些则没有:
reinterpret_cast
and const_cast
(或等效的 C 样式转换,例如问题中的转换)和转换为或从void*
将简单地重新解释指针值,而无需任何成本。static_cast
如果有多个基类,则指针到基类和指针到派生类之间的转换(隐式地,或使用或等效的 C 样式转换)可能需要向指针值添加固定偏移量。dynamic_cast
将做大量的工作来根据所指向对象的动态类型查找指针值。从历史上看,一些体系结构(例如 PDP-10)对指针到字节和指针到字有不同的表示。那里的转换可能会有一些运行时成本。
unsigned long long *x;
/* fill data to x*/
for (int i = 0; i < 1000*1000*1000; i++)
{
A[i]=foo((unsigned char*)x+i); // bad cast
}
请记住,机器只知道内存地址、数据和代码。其他所有内容(例如类型等)只有编译器(帮助程序员)知道,并且执行所有指针运算,只有编译器知道每种类型的大小......等等。
在运行时,将一种指针类型转换为另一种指针类型不会浪费任何机器周期,因为转换不会在运行时发生。所有指针都被视为 4 字节长(在 32 位机器上),不多也不少。
这完全取决于您的底层硬件。
在大多数机器架构上,所有指针都是字节指针,字节指针和字节指针之间的转换是空操作。在某些架构上,指针转换在某些情况下可能需要额外的操作(例如,有些机器使用基于字的地址,将字指针转换为字节指针或反之亦然需要额外的操作)。
此外,这通常是一种不安全的技术,因为编译器无法对您正在执行的操作执行任何健全性检查,并且您最终可能会覆盖您没有预料到的数据。