函数的限制限定指针参数是允许编译器优化that function:

int f(const int *restrict p) {
  int n=*p;
  printf("Debug\n");
  return *p==n;
}

在这里,带有-03的Clang 17将只返回1,而不重新判断*p,也不执行比较. 如果没有restrict,则必须重新判断*p,并进行比较.这是因为printf是一个外部函数,编译器不知道它是否会修改*p.实际上,如果p恰好指向文件位置指示符stdout,则printf实际上要修改它.

因此,作为一般原则,如果您希望最大限度地优化the function,并且您的函数曾经调用过外部函数,则声明所有参数restrict并不是没有意义的.当然,这对调用者施加了严格的限制,因为它要求调用者promise 不会使用指向意外位置的别名指针来调用您的函数.

我的问题比这个观察更深入.我问restrict是不是也可以优化the callers of your function

int f(const int *restrict p);
int caller(int *p) {
  int n=*p;
  f(p);
  return *p==n;
}

在我看来,这似乎向编译器保证了f不会修改*p,因此它可以省略重新判断*p和比较,并且可以安全地返回1.Clang和GCC都不做这种优化.

我知道他们不优化调用者是可以的.我问的是,是否允许对其进行优化.(这主要是关于程序的语义的问题,而不是关于性能的问题.)

推荐答案

在此代码中:

int f(const int *restrict p);
int caller(int *p) {
  int n=*p;
  f(p);
  return *p==n;
}

编译器不能得出*pf的执行期间没有改变的结论,因为下面的代码显示了f的定义和对caller的调用,其中定义了行为(不违反restrict要求),但*p的值在f的执行期间改变:

extern int m[2];

int f(const int * restrict p)
{
    m[0] = 3;
    return p[1];
}

void bar(void)
{
    caller(m);
}

在本例中,当调用f时,p指向m[0],因此*pm[0].由于f改变m[0],因此*p的值在f的执行期间改变,但这不违反restrict要求,因为restrict定义仅在p被直接或间接用于访问对象的情况下施加要求.C 2018 6.7.3.1 4表示:

在每次执行B的过程中,让L&L基于P的任何左值.如果L被用来访问它指定的对象X的值,并且X也被修改(以任何方式),则以下要求适用:…

在上面的f中,m[0]只能通过左值m[0]访问,而不能通过地址基于p的任何左值访问.因此,restrict的前提条件永远不会出现在m[0]中,因此它不会对m[0]是否或如何修改施加任何约束.

C++相关问答推荐

从内联程序集调用Rust函数和调用约定

如何使用Python C API实现多线程程序?

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

使用NameSurname扫描到两个单独的字符串

将 struct 变量赋给自身(通过指针取消引用)是否定义了行为?

在传统操作系统上可以在虚拟0x0写入吗?

自定义变参数函数的C预处置宏和警告 suppress ?

fwrite无法写入满(非常大)缓冲区

我的程序在收到SIGUSR1信号以从PAUSE()继续程序时总是崩溃()

Caesar密码调试:输出文本末尾的问号和随机字符

C语言中神秘的(我认为)缓冲区溢出

Go和C中的数据 struct 对齐差异

运行时错误:在索引数组时加载类型为';char';`的空指针

基于蝶数恰好有8个除数的事实的代码

在git补丁中自动添加C的宏

无法将字符串文字分配给 C 中的字符数组

使用fread()函数读取txt文件

如何转义包含指令中的字符?

子进程不会修改父进程中的统计信息

多行表达式:C 编译器如何处理换行符?