只是想知道是否有人会为我确认一些别名规则。
我知道别名(即加载存储问题)可能导致以下类型的代码不是最理想的,因为我们不能假设x, y, z
不重叠:< / p>
// case 1:
void plus(size_t n, double *x, double *y, double *z)
{
for (size_t i = 0; i != n; ++i)
z[i] = x[i] + y[i];
}
我知道有一个C关键字__restrict
提示编译器它不应该考虑重叠的情况,因此可能生成更好的代码:
// case 2:
void plus(size_t n, double *__restrict x, double *__restrict y, double *__restrict z)
{ // as above... }
但是别名如何处理C ++样式代码,我们将处理通过引用传递的容器对象,而不是上面带有原始指针的C类示例?
例如,我假设如果我们执行以下操作会出现别名问题:
// case 3:
void plus(std::vector<double> &x, std::vector<double> &y, std::vector<double> &z)
{ // similar to above... }
要转移到一个不那么简单的例子,如果容器中的基础数据类型不同,它会有什么不同吗?在实现级别,大多数容器使用指针动态管理存储,因此我不清楚编译器如何确保以下不会出现别名:
// case 4:
void foo(std::vector<mytype1> &x, std::vector<mytype2> &y)
{ // interwoven operations on x, y... }
我不是想进行微优化,但我想知道目前是否更好地将限制指针传递给容器,而不是引用。
编辑:要清除一些术语,如上所述:restrict
是C99关键字。在各种编译器中也有__restrict
和__restrict__
,但它们都做同样的事情。
答案 0 :(得分:7)
根据strict-aliasing rule,不允许使用指向不同类型的指针(char*
和朋友除外)对同一内存进行别名,因此只有在其中一种类型为a时,情况4才适用char*
。
案例3虽然与案例1没有什么不同,因为引用是作为我知道的所有编译器的指针实现的,尽管标准并没有要求它,并且实现可以自由地提出其他内容。
答案 1 :(得分:4)
它对C ++并不具体。考虑一下这个C99位:
struct vector {
double* data;
size_t n;
};
void
plus(struct vector* restrict x, struct vector* restrict y, struct vector* restrict z)
{
// same deal as ever
}
在这里,restrict
购买的很少:x->data
,y->data
和z->data
都是double*
,并且可以使用别名。这与使用restrict时的情况1,甚至完全相同。
如果C ++中有restrict
个关键字(或使用扩展程序时),最好的选择可能是plus(vecA.size(), &vecA[0], &vecB[0], &vecB[0])
,使用与案例2相同的plus
。事实上,现在可以使用不带restrict
的C89风格界面,但使用封面下的关键字。