首页 > 解决方案 > 有没有办法告诉 C 编译器指针没有别名存储?

问题描述

如果 C 编译器知道指针没有别名,它可以执行许多优化。例如,如果我编译以下函数gcc -O2

int f_noalias(int *arr, int x)
{
    int res = 0;
    int *p = &arr[17];
    *p = x;
    res += *p;
    res += *p;
    return res;
}

编译器知道读取*p将始终评估为,x因此生成的代码等效于为以下函数生成的代码:

int f_noalias2(int *arr, int x)
{
    int *p = &arr[17];
    *p = x;
    return 2*x;
}

但是,如果编译器认为指针可能有别名,则不再执行此优化。例如,如果我们修改f以便在读取 to 之间调用未知函数*p,则生成的代码将取消引用p两次。编译器假定该read_arr函数可能已经修改了p指向的值。

int f_withalias(int *arr, int x)
{
    int res = 0;
    int *p = &arr[17];
    *p = x;
    res += *p;
    read_array(arr);
    res += *p;
    return res;
}

在我的特定程序中,当f函数运行时p,它持有的指针是唯一写入该arr数组元素的指针。在这段时间内,代码中的其他函数可能会读取arr,但不会写入。(他们可能会在完成运行arr后写入其他值。)f

所以现在我有三个问题:

第一:有没有办法我可以声明我的变量来给 C 编译器这个提示?我尝试添加一个限制注释,p但生成的代码gcc -O2与生成的代码相同f_withalias

int f_restrict(int *arr, int x)
{
    int res = 0;
    int * restrict p = &arr[17];
    *p = x;
    res += *p;
    read_array(arr);
    res += *p;
    return res;
}

第二:我在这里使用限制的尝试有效吗?我的理解是,restrict 意味着没有其他指针可以为 p 别名,无论是读取还是写入。但在我的情况下,该read_arr函数显然也可以访问指向的arr数组。p

第三:如果上一个问题的答案是“否”,我可以尝试一些不同的东西来代替restrict吗?

基本上,我需要确保如果我这样做*p = xf那么从arr[17]. 但是,我希望 GCC 可以随意优化 tox = *p; y = *p之类的东西x = *p; y = x,即使两次读取之间存在函数调用。

标签: crestrictrestrict-qualifier

解决方案


第一:有没有办法我可以声明我的变量来给 C 编译器这个提示?

int * restrict p = &arr[17];断言 onlyp和基于的指针表达式p将用于p在块的持续时间内访问任何指向的对象(不以任何方式修改的对象除外)。这可以优化res += *p;您提出的建议。GCC 没有如此优化的事实是 GCC 中的一个质量问题。

第二:我在这里使用限制的尝试有效吗?…基本上,我需要确保如果我在 f 中执行 *p = x,那么从 arr[17] 读取的其他函数会立即注意到该写入。

后一个属性不是restrict. p声明restrictarr[17]修改的事实意味着在执行包含 的块期间不应使用p不基于的指针来访问,甚至不用于读取。因此,如果确实读取了某些内容(使用,而不是基于),则将违反断言。parr[17]pread_arrayarr[17]arrprestrict


推荐阅读