int foo(void *restrict ptr1, void *restrict ptr2)
{
  if (ptr1 == ptr2) {
    return 1234;
  } else {
    return 4321;
  }
}

restrict表示指针指向的内存不会被任何其他指针别名.鉴于此,ptr1ptr2不能指向相同的区域,因此比较是重言式的,foo()在所有情况下都应该返回4321.

然而,clanggcc不是这样看的(https://godbolt.org/z/fvPd4a1vd).这是一个遗漏的优化,还是有其他原因?

推荐答案

根据第C99 Draft N1256条第6.7.3条:

通过限制限定指针访问的对象具有特殊的关联 用那个指针.此关联在下面的6.7.3.1中定义,要求所有访问 该对象直接或间接地使用该特定指针的值 使用RESTRICE限定符(如寄存器存储类)是为了提升 优化,并从所有预处理转换中删除限定符的所有实例 构成一致性程序的单元不改变其含义(即,可观察 行为).

将指针声明为restrict可确保编译器不会有其他指针修改受限指针所指向的内存位置.

但是,您仍然可以有两个指向同一内存区域的受限指针.

同样,C99 Draft N1256个中的6.7.3.1中的示例3也派上了用场:

void h(int n, int * restrict p, int * restrict q, int * restrict r)
{
    int i;
    for (i = 0; i < n; i++)
        p[i] = q[i] + r[i];
}

以下是该标准的 comments :

[h的函数参数声明]说明了如何通过两个受限指针为未修改的对象设置别名.具体地说,如果a和b是不相交的数组,则h(100,a,b,b)形式的调用定义了行为,因为数组b在函数h内不被修改.

所以,你的问题的答案是:不,这不是GCC或Clang错过的优化.


正如Peter在 comments 中指出的那样,可能缺少基于以下与受限指针相关的未定义行为的优化:

通过限制限定指针访问已修改的对象 常量限定类型,或通过限制限定指针和另一个 两者不是基于同一对象(6.7.3.1).

从本质上讲,如果没有使用两个受限指针来修改它们所指向的数据(这是您的函数foo和我的答案中的函数h的情况),即使它们指向相同的内存区域,也不会发生未定义的行为.因此,编译器不能在编译时说明它们的值:它们可以是相同的,也可以不同.

然而,一种不同的情况如下:

int compare_pointers(int *restrict ptr1, int *restrict ptr2)
{
    *ptr1 = 0;
    *ptr2 = 1;
    if (ptr1 != ptr2) {
        return 1234;
    } else {
        return 4321;
    }
}

由于这两个指针都用于修改数据,因此它们必须与不同的内存区域相关联,否则将出现未定义的行为(这是关键字restrict的结果),因此优化可能会删除后续比较并返回1234.然而,GCC和克兰都没有像彼得的 comments 所显示的那样进行这样的优化.

C++相关问答推荐

有什么方法可以检测SunOS上的SparcWorks吗?

Pure Win32 C(++)-除了替换控件的窗口程序之外,还有其他方法可以在输入时禁用按钮吗?

当打印字符串时,为什么在c中没有使用常量限定符时我会收到警告?

如何将已分配的数组(运行时已知的大小)放入 struct 中?

为什么在Linux(特别是Ubuntu 20.04LTS)上,POSIX共享内存对象在重启后仍然存在,然后突然变成了根用户?

如何将字符串argv[]赋给C中的整型数组?

拥有3x3二维数组并访问数组[1][3]等同于数组[2][0]?

如何使用[BTStack]BLE发送大型(>;2kb)信息包

进程在写入管道时挂起

S将C语言宏定义为自身的目的是什么?(在glibc标题中看到)

如何使用C++在控制台中以彩色打印被阻止的客户端

C语言中的外部关键字

将数组插入数组

Fscanf打印除退出C代码为1的程序外的所有内容

如何在不更改格式说明符的情况下同时支持双精度和长双精度?

赋值两侧的后置增量,字符指针

Malloc和对齐

C中2个数字的加法 - 简单的人类方法

为什么写入关闭管道会返回成功

我们可以在不违反标准的情况下向标准函数声明添加属性吗?