(Все ссылки относятся к N1256, т.е. C99 плюс технические исправления (TC3).)
Формальное определение restrict
дано в §6.7.3.1. Я цитирую наиболее важный подпункт ниже. P
— это указатель с указанием restrict
на тип T
, областью действия которого является блок B
. Говорят, что выражение указателя E
основано на P
, если оно зависит от значения самого P
, а не от значения, на которое указывает P
.
Во время каждого выполнения B
пусть L
будет любым lvalue, которое имеет &L
на основе P. Если L
используется для доступа к значению объекта X
, который он обозначает, и X
также изменяется (любым способом), то применяются следующие требования. :
T
не должно быть константным.
- Каждое другое lvalue, используемое для доступа к значению
X
, также должно иметь свой адрес, основанный на P
.
- Каждый доступ, который изменяет
X
, должен также рассматриваться как модифицирующий P
для целей настоящего подпункта.
- Если
P
присваивается значение выражения указателя E
, которое основано на другом ограниченном объекте указателя P2
, связанном с блоком B2
, то либо выполнение B2
должно начаться до выполнения B
, либо выполнение B2
должно закончиться до выполнения назначение.
Если эти требования не выполняются, то поведение не определено.
Давайте посмотрим, что правила говорят о доступе к частям array
bar
в foo
. Начнем с array
, указателя с ограничениями, объявленного в списке параметров bar
. Для ясности я буду альфа-конвертировать параметры foo
:
void foo(float* b, float c, unsigned int n) { /*modify b[i]*/ }
Хранилище, на которое указывает array
, также модифицируется через b
. Со вторым пунктом все в порядке, так как &array[i*n]
эквивалентно array+(i*n)
(см. §6.5.3.2).
Если бы b
было ограничено, нам пришлось бы проверить четвертый пункт списка с P
← b
, B
← foo
, P2
← array
, B2
← bar
. Так как B
вложен внутри B2
(функции ведут себя здесь как встроенные, см. §6.7.3.1.11), первое условие выполнено. Существует также один экземпляр третьего пункта (доступ к b[i]
в foo
), который не является проблемой.
Однако b
не является ограниченным. Согласно §6.3.2.3.2, «для любого квалификатора q указатель на тип, не уточненный q, может быть преобразован в указатель на q-уточненная версия типа; значения, хранящиеся в исходном и преобразованном указателях, должны сравниваться равными». Таким образом, преобразование из array+(i*n)
в b
четко определено и имеет очевидный смысл, поэтому поведение программы определено. Кроме того, поскольку b
не является restrict
-квалифицированным, ему не нужно подчиняться какому-либо условию линейности. Например, следующий foo
допустим в сочетании с bar
:
void qux(float *v, float *w) {
v[0] += w[0];
}
void foo(float* b, float c, unsigned int n)
{
qux(b,b);
}
ДОБАВЛЕНО: чтобы решить вашу конкретную проблему «в bar(), где вы передаете адрес части массива в foo()», это не проблема: restrict
применяется к указателю, а не к массив, и вы можете выполнять над ним арифметические операции (пункт 2).
person
Gilles 'SO- stop being evil'
schedule
04.10.2010