Является ли ключевое слово/определитель 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
не накладывает никаких требований относительно их использования для доступа к объектам, на которые они указывают.
Я сомневаюсь, что это имело бы смысл с точки зрения удобства использования, если бы оно было «транзитивным», как вы предлагаете.