指针转换是否昂贵?

4
指针转换是否被认为是昂贵的?(例如,将指针/地址转换需要多少 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);

};

11
类型仅在编译时存在。也就是说,这种指针转换通常不是一个好主意。 - R. Martinho Fernandes
4个回答

9
在大多数机器码语言中,只有1种“类型”的指针,因此在它们之间进行转换是不需要代价的。请记住,C++类型实际上仅存在于编译时。
真正的问题是这种代码可以违反严格别名规则。您可以在其他地方阅读更多相关信息,但基本上编译器要么通过未定义的行为产生不正确的代码,要么被迫做出保守的假设,从而产生较慢的代码。(请注意,char*和其它类似指针类型在未定义行为部分有一定的豁免)
优化程序通常需要对指针变量做出保守的假设。例如,已知变量x的值为5的常量传播过程将无法在向另一个变量(例如*y = 10)赋值后继续使用此信息,因为*y可能是x的别名。在y = &x等赋值之后,这可能是情况。由于*y的赋值,x的值也会发生变化,因此传播信息x为5到*y = 10后面的语句将是潜在错误的(如果*y确实是x的别名)。但是,如果我们有关于指针的信息,则常量传播过程可以提出类似以下的查询:x是否可能是*y的别名?然后,如果答案是否定的,则可以安全地传播x = 5。别名对代码重排序也产生了影响。如果编译器确定x不是*y的别名,则可以将使用或更改x值的代码移动到*y = 10之前,如果这将改善调度或使更多循环优化得以执行。
为了以可预测的方式启用此类优化,C编程语言(包括其较新的C99版,参见第6.5节,第7段)的ISO标准规定,除某些例外情况外,不同类型的指针引用相同的内存位置是非法的。这个规则称为“严格别名”,有时能够产生惊人的性能提升,但已知会破坏其他有效代码。 几个软件项目故意违反了C99标准的这部分内容。例如,Python 2.x是为了实现引用计数而这样做的[2],并需要更改Python 3中的基本对象结构才能启用此优化。Linux内核这么做是因为严格别名会导致内联代码的优化问题[3]。在这种情况下,在使用gcc编译时,将运用选项-fno-strict-aliasing来防止可能会产生意外代码的不必要的优化。
参考:http://en.wikipedia.org/wiki/Aliasing_(computing)#Conflicts_with_optimizationWhat is the strict aliasing rule?

3
这段代码没有违反严格别名规则,因为“unsigned char*”是其中一个特殊例外(极少数情况之一)。 - MSalters
1
但是,如果我没记错的话,char 类型可以安全地别名任何其他类型。因此,原始代码仍然是正确的。 - rodrigo
@MSalters 是的,我刚意识到这一点。虽然我想它们仍然会阻止优化。 - Pubby
大多数情况下,强制类型转换是无操作的。然而,在C++中向上或向下转换多重或虚拟继承类层次结构可能涉及一些加法或减法,以生成确实指向正确内存部分的指针。 - user420442

8
在任何体系结构中,你可能会遇到的所有指针类型都具有相同的表示形式,因此在表示相同地址的不同指针类型之间进行转换不会产生运行时成本。这适用于C中的所有指针转换。
在C++中,一些指针转换需要成本,而另一些则不需要:
- reinterpret_cast和const_cast(或等效的C风格转换,例如问题中的转换)以及与void*之间的转换将简单地重新解释指针值,没有成本。 - 指向基类和派生类的指针之间的转换(无论是隐式的还是使用static_cast或等效的C风格转换),如果存在多个基类,则可能需要向指针值添加固定偏移量。 - dynamic_cast将执行大量的工作来查找基于所指向对象的动态类型的指针值。
历史上,某些体系结构(例如PDP-10)具有指向字节和指向字的指针的不同表示形式;在那里进行转换可能会产生一些运行时成本。

1
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位机器上),没有更多也没有更少。

1

这完全取决于您的底层硬件。

大多数机器架构中,所有指针都是字节指针,而在字节指针和字节指针之间转换是不需要任何操作的。在某些架构中,指针转换可能在某些情况下需要额外的操作(例如,有些机器使用基于字的地址,将字指针转换为字节指针或反之亦然将需要额外的操作)。

此外,这通常是一种不安全的技术,因为编译器无法对您所做的事情进行任何合理性检查,您可能会意外地覆盖您没有预料到的数据。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接