C ';限制';关键字-为什么允许从外部限制变量分配到内部限制变量?

C ';限制';关键字-为什么允许从外部限制变量分配到内部限制变量?,c,pointers,restrict,restrict-qualifier,C,Pointers,Restrict,Restrict Qualifier,首先是一些参考文献。第6.7.3节中关于限制的说明如下: 通过限制限定指针访问的对象具有 与该指针的特殊关联。此关联在中定义 6.7.3.1要求对该对象的所有访问直接或间接使用该特定指针的值。117)预期 使用限制限定符(如寄存器存储类)是为了 促进优化,并从中删除限定符的所有实例 构成一致性程序的所有预处理翻译单元 不会改变其含义(即,可观察到的行为) 然后(§6.7.3.1“限制的正式定义”): 设D为提供方法的普通标识符的声明 将对象P指定为类型T的限制限定指针 如果D出现在块内,并且没有

首先是一些参考文献。第6.7.3节中关于限制的说明如下:

通过限制限定指针访问的对象具有 与该指针的特殊关联。此关联在中定义 6.7.3.1要求对该对象的所有访问直接或间接使用该特定指针的值。117)预期 使用
限制
限定符(如
寄存器
存储类)是为了 促进优化,并从中删除限定符的所有实例 构成一致性程序的所有预处理翻译单元 不会改变其含义(即,可观察到的行为)

然后(§6.7.3.1“限制的正式定义”):

D
为提供方法的普通标识符的声明 将对象
P
指定为类型
T
的限制限定指针

如果
D
出现在块内,并且没有存储类
extern
, 让
B
表示块。如果参数列表中出现
D
函数定义的声明,让
B
表示相关的 块否则,让
B
表示main块(或 在程序启动时以独立的方式调用的任何函数 环境)

在下文中,指针表达式
E
被称为基于对象
p
if (在 计算
E
)修改
P
以指向数组对象的副本 它以前指向的将更改
E
.119)注释的值 仅为具有指针类型的表达式定义了“基于”

在每次执行
B
的过程中,让
L
为基于
P
。如果使用
L
访问对象
X
的值,则 指定,并且还修改了
X
(通过任何方式),然后 适用要求:
T
不得为合格。其他左值 用于访问
X
值的地址也应基于
P
。 每个修改
X
的访问也应被视为修改
P
,例如 本款的目的。如果
P
被赋值为 基于另一个受限指针的指针表达式
E
对象
P2
,与块
B2
关联,然后执行
B2
应在执行
B
之前开始,或应在执行
B2
之前开始 在任务之前结束。如果不满足这些要求,则 该行为未定义

如图所示,这说明了规则(标准中的示例4):

现在,我的第一个问题是:为什么可以从外部受限指针分配到内部受限指针

我的理解是,没有任何东西禁止这样做,因为它有明显的别名:

int * restricted x = /* ... */ ;

{
    int * restricted y = x;
    *x = 3;
    printf("%d\n", *y); // 3
    *y = 4;
    printf("%d\n", *x); // 4
}
当然,别名集仅限于两个指针


因此,我的第二个问题:从外部到内部(允许),但从内部到外部(禁止,例如上文第一个示例中的
p1=q1;
)分配的差异是什么?

这些都不是未定义的行为。您可以随意在限制指针之间赋值。如果以错误的方式指定限制指针指向的对象,则可能会发生未定义的行为

在第二个例子中,指针y是从x派生出来的,所以首先赋给*x,然后赋给与*y相同的变量也可以


你有没有想过“限制”应该达到什么目的,而不是阅读法律术语

我认为这些规则旨在满足两个目标:

  • 允许创建临时指针,类似于将参数传递给函数调用时自然创建的指针,而无需将使用指针的代码移动到物理上独立的函数中

  • 确保显示指针派生的图形没有循环(这意味着如果指针x派生自y或直接或间接派生自y的任何对象,则y不能派生自x或直接或间接派生自x的任何对象)。虽然规则可能比实现#2绝对必要的规则更严格,但满足第二个要求的几乎所有有用的案例也都满足编写的规则,并且编译器很难从
    restrict
    中获益


  • restrict
    限定符远非完美,但它仍然比大多数替代品要好。

    请更改它,使其能够真正编译。在函数的参数列表中,不是唯一可以将指针声明为
    restrict
    的地方吗?@fuzzxl不,标准没有这样说,而且它在所有警告和严格的标准遵从性启用的情况下都能很好地工作。
    restrict
    的语义如果描述允许编译器执行的操作比禁止程序员执行的操作更容易理解,但我认为基本思想是
    restrict
    允许编译器的行为,就像任何通过
    restrict
    指针的读取都是在指针赋值和逻辑读取发生点之间的执行序列中的任何位置执行的一样,和写操作允许在指针生命周期内的任何时间发生,超出逻辑写操作发生的位置,除非读和写操作不能发生…如果您有两个函数
    void foo(int*restrict a,int*
    
    int * restricted x = /* ... */ ;
    
    {
        int * restricted y = x;
        *x = 3;
        printf("%d\n", *y); // 3
        *y = 4;
        printf("%d\n", *x); // 4
    }