C 人类能从这个限定词中得到什么?

C 人类能从这个限定词中得到什么?,c,c99,keyword,restrict-qualifier,C,C99,Keyword,Restrict Qualifier,如果我正确使用了C99restrict关键字,那么使用它限定指针就是保证它引用的数据不会通过别名在编译器后面修改 相比之下,我对const限定符的理解是编译器强制的文档,即给定对象不会在编写代码的人背后被修改。编译器可能会得到一个提示作为一个副作用,但作为一个程序员,我真的不在乎 以类似的方式,在函数原型中考虑“限制< /代码>限定符是否适合于用户在调用期间确保独占访问(“避免别名”或可能更强)的要求?是否应将其用作“文件” 另外,restrict限定指针而不是它所指向的数据(正如const所做

如果我正确使用了C99
restrict
关键字,那么使用它限定指针就是保证它引用的数据不会通过别名在编译器后面修改

相比之下,我对
const
限定符的理解是编译器强制的文档,即给定对象不会在编写代码的人背后被修改。编译器可能会得到一个提示作为一个副作用,但作为一个程序员,我真的不在乎

以类似的方式,在函数原型中考虑“<代码>限制< /代码>限定符是否适合于用户在调用期间确保独占访问(“避免别名”或可能更强)的要求?是否应将其用作“文件”

另外,
restrict
限定指针而不是它所指向的数据(正如
const
所做的那样)这一事实是否需要理解


编辑:我最初认为
restrict
可能会对线程代码产生影响,但这似乎是错误的,因此我从问题中删除了对线程的引用,以避免混淆读者。

更熟悉该标准的人可能会给出更好的答案,但我会尝试一下

“数据不会在编译器背后被修改”听起来更像是“volatile”的反面

“const”表示数据不会在程序员面前被修改;也就是说,她不能通过标记为“const”的能指修改数据(我写“能指”,因为在
int const*pi
中,
pi
不是const,而是
*pi
是)。数据可以通过另一个符号进行修改(毕竟,非常量数据可以作为常量数据传递给函数)


“限制”限定指针是关键。指针是C中别名数据的唯一方法,因此它们是通过两个不同名称访问某些数据的唯一方法。“限制”是指将数据访问限制在一条访问路径上。

这可能是一个非常狭窄的领域的例子,但Altera的Nios II平台是一个软核微控制器,您可以在FPGA中自定义。然后,在该micro的C源代码中,您可以使用C-to-hardware工具使用自定义硬件而不是软件来加速内部循环

在这里,使用
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。至少在这种情况下,
restrict
根本不适合人类消费。另请参见,其中第一行显示

在C程序中适当地使用
restrict
限定符可以使编译器产生更快的可执行文件


如果有人有兴趣阅读更多关于C2H的内容,请讨论优化C2H结果。
\uuuu restrict\uuuuu
一节在第20页。

关于restrict关键字最好的“直觉”是,它保证(由程序员向编译器)在指针的生命周期内,通过该指针访问的内存只能通过该指针访问,而不能通过另一个指针、引用或全局地址访问。因此,重要的是,它是指针和内存的属性,将指针和内存绑定在一起,直到指针超出范围。

您的理解基本正确。
restrict
限定符简单地表示,由如此限定的指针访问的数据仅由该指针访问。它适用于读和写

编译器不关心并发线程,它不会以任何不同的方式生成代码,您可以随意修改自己的数据。但它确实需要知道什么指针操作可能会改变什么全局内存

Restrict
还附带了一个API警告,警告人类给定的函数是在假定无关联参数的情况下实现的

就编译器而言,用户无需锁定。它只想确保在没有
restrict
限定符的情况下,通过编译器应该生成的代码正确读取应该被删除的数据。添加
restrict
可将其从该问题中解放出来

最后,请注意,编译器可能已经在更高的优化级别上基于数据类型分析了可能的别名,因此
restrict
对于具有指向同一类型数据的多个指针的函数来说非常重要。您可以从本主题中吸取教训,并确保通过
联合
进行任何故意的别名处理

我们可以看到
restrict
的作用:

void move(int *a, int *b) {     void move(int *__restrict a, int *__restrict b) {
    a[0] = b[0];                    a[0] = b[0];
    a[1] = b[0];                    a[1] = b[0];
}                               }
    movl    (%edx), %eax            movl    (%edx), %edx
    movl    %eax, (%ecx)            movl    %edx, (%eax)
    movl    (%edx), %eax            movl    %edx, 4(%eax)
    movl    %eax, 4(%ecx)

在右边的列中,使用
restrict
,编译器不需要从内存中重新读取
b[0]
。它能够读取
b[0]
并将其保存在寄存器
%edx
,然后将寄存器存储两次到内存中。在左列中,它不知道
a
的存储是否已更改
b

Chris Dodd对关键字的描述是正确的。在某些平台上,由于性能原因,它可能非常重要,因为它让编译器知道,一旦它通过该指针将数据加载到寄存器上,就不需要再次这样做。如果没有这一保证,编译器必须在每次写入任何其他可能的别名指针时通过指针重新加载数据,这可能会导致称为错误的严重管道暂停

const
restrict
是不同的概念,而
const
并不意味着
restrict
。const
所说的是,您不会在该函数的范围内通过该指针进行写入。
const
指针仍可能有别名。对于e
int foo( const int *a, int * b )
{
   *b *= 2;
   return *a + *b; // induces LHS: *a must be read back immediately
                   // after write has cleared the store queue
}
int x = 3;
foo( &x, &x );  // returns 12
void f(int *a, int *b, int *c) { 
    for (int i=0; i<*a; i++)
        *b += c[i];
}