Является ли ключевое слово/определитель C restrict транзитивным через несколько уровней косвенности указателя?
Например, учитывая следующий фрагмент:
struct Bar {
int b;
};
struct Foo
{
struct Bar* bar;
};
int examineFoos(struct Foo* restrict foo1, struct Foo* restrict foo2)
{
foo1->bar->b = 1;
return foo2->bar->b;
}
int main(void)
{
struct Bar bar = { 0 };
struct Foo foo1 = { .bar = &bar };
struct Foo foo2 = { .bar = &bar };
int val = examineFoos(&foo1, &foo2);
return val;
}
Вызывает ли это неопределенное поведение и может val == 0, потому что оно нарушает restrict при доступе к bar, или это действительный код, и мы всегда получаем val == 1, потому что restrict не применяется транзитивно через указатель bar?





Условия restrict для foo1 и foo2 не накладывают никаких ограничений на использование структур, на которые указывают foo1->bar и foo2->bar, как показано ниже.
Требования, предъявляемые к программе restrict, указаны в C 2018 6.7.3.1 4. Эти требования применяются только «Если L используется для доступа к значению объекта X, который он обозначает, и X также модифицируется (любыми способами), », где L — это lvalue, для которого &L основано на P, обозначенном как ограниченный указатель для ввода T во время выполнения связанного блока B. Основание указано в 6.7.3.1 3:
В дальнейшем выражение-указатель
Eназывается основанным на объектеP, если (в какой-то точке последовательности выполненияBперед вычислениемE) модифицируетсяP, чтобы указать на копию объекта массива, на который оно ранее указывало. изменит значениеE.
Указатели с ограничением — это foo1 и foo2. Подумайте, может ли foo1->bar или foo2->bar основываться на foo1 или foo2. Другими словами, если E равно foo1->bar или foo2->bar, изменится ли его значение, если будет произведена модификация, описанная выше?
Предположим, в различных точках тела examineFoos (блока B) мы делаем копии структур, на которые указывают foo1 и foo2, а затем меняем foo1 и foo2, чтобы они указывали на соответствующие копии. Будет ли значение foo1->bar после этого вмешательства таким же, как и раньше? Да, потому что значение foo1->bar определяется байтами, которые его представляют, и эти байты были скопированы. Аналогично, foo2->bar будет иметь то же значение.
Следовательно, foo1->bar и foo2->bar не основаны на foo1 или foo2, поэтому спецификация restrict не накладывает никаких требований относительно их использования для доступа к объектам, на которые они указывают.
Я сомневаюсь, что это имело бы смысл с точки зрения удобства использования, если бы оно было «транзитивным», как вы предлагаете.