Ключевое слово "ограничивать" - почему разрешено назначать из внешней ограниченной переменной внутреннюю ограниченную переменную?
Сначала несколько ссылок. C99 Standard говорит об restrict
в разделе 6.7.3:
Объект, к которому осуществляется доступ с помощью указателя с ограничением, имеет специальная ассоциация с этим указателем. Эта ассоциация, определенная в 6.7.3.1 ниже, требует, чтобы все обращения к этому объекту прямо или косвенно использовали значение этого конкретного указателя. 117)использование квалификатора restrict
(например, класс хранения register
) - это содействовать оптимизации и удалять все экземпляры квалификатора из все единицы перевода для предварительной обработки, составляющие соответствующую программу не меняет его значения (т.е. наблюдаемого поведения).
И затем (§6.7.3.1 "Формальное определение restrict
" ):
Пусть D
является объявлением обычного идентификатора, который предоставляет средства о назначении объекта P
в качестве указателя с ограничениями типа T
.
Если D
появляется внутри блока и не имеет класса хранения extern
, пусть B
обозначает блок. Если в списке параметров появляется D
объявления определения функции, пусть B
обозначает ассоциированный блок. В противном случае пусть B
обозначает блок main (или блок любая функция вызывается при запуске программы в автономном режиме среда).
В дальнейшем выражение указателя E
называется основанным на объекте P
, если (в некоторой точке последовательности при выполнении B
до оценка E
), изменяющая P
, чтобы указать на копию объекта массива в который он ранее указал, изменит значение E
. 119) Примечание. что '' based '' определяется только для выражений с типами указателей.
При каждом выполнении B
пусть L
будет любое значение l, которое имеет &L
на основе P
. Если L
используется для доступа к значению объекта X
, чтобы оно и X
также изменяется (любыми способами), то следующие применяются требования: T
не должен быть const-квалифицированным. Все остальные значения используемый для доступа к значению X
, также должен иметь свой адрес на основе P
. Каждый доступ, который изменяет X
, должен также быть изменен P
, для цели этого подпункта. Если P
присвоено значение a выражение указателя E
, которое основано на другом ограниченном указателе объект P2
, связанный с блоком B2
, то либо выполнение B2
начинаются до выполнения B
, или выполнение B2
должно до назначения. Если эти требования не выполняются, то поведение undefined.
Поскольку некоторые указали, это иллюстрирует правила (пример 4 из стандарта):
{
int * restrict p1;
int * restrict q1;
p1 = q1; // undefined behavior
{
int * restrict p2 = p1; // valid
int * restrict q2 = q1; // valid
p1 = q2; // undefined behavior
p2 = q2; // undefined behavior
}
}
Теперь мой первый вопрос заключается в следующем: почему это нормально назначать из внешнего ограниченного указателя на внутренний?
Я понимаю, что ничто не запрещает это, у которого есть ясное наложение:
int * restricted x = /* ... */ ;
{
int * restricted y = x;
*x = 3;
printf("%d\n", *y); // 3
*y = 4;
printf("%d\n", *x); // 4
}
Конечно, набор псевдонимов ограничен двумя указателями.
Следовательно, мой второй вопрос: какая разница назначается от внешнего к внутреннему (разрешенному), но не от внутреннего к внешнему (запрещена, например, p1 = q1;
в первом примере выше)?
Ответы
Ответ 1
Я думаю, что правила предназначены для удовлетворения двух целей:
-
Разрешить создание временного указателя, аналогичного тому, что, естественно, будет создано, когда аргумент передается вызову функции, не требуя, чтобы код с использованием указателя был перемещен в физически отдельную функцию.
-
Убедитесь, что график, показывающий вывод указателя, будет свободен от циклов (это означает, что если указатель x получен из y или что-либо, прямо или косвенно полученное из y, y не может быть выведено из x или ничего, что непосредственно или косвенно полученным из x). Хотя правила могут быть более жесткими, чем это было бы абсолютно необходимо для достижения № 2, почти все полезные случаи, которые удовлетворяли бы этому второму требованию, также удовлетворяли бы правилам, как написано, а компиляторы с трудом получали бы большую выгоду от restrict
в случаях что нет.
Квалификатор restrict
далек от совершенства, но он все же лучше, чем большинство альтернатив.
Ответ 2
Ни одно из них не является undefined. Вы можете назначить между указателями ограничений все, что вам нравится. undefined поведение может возникать, если вы назначаете объекты, на которые указали, указав указатели указателей неправильно.
И в вашем втором примере указатель y получается из x, поэтому сначала назначить * x, затем той же переменной, что и * y, тоже хорошо.
Вместо того, чтобы читать легализацию, действительно ли вы думали о том, что "ограничение" должно достичь?