Является ли это недопустимым использованием указателей restrict?
предположим, что у меня большой массив, в который я вычисляю индекс и передаю второй функции. В качестве простого примера, что-то вроде:
void foo(float* array, float c, unsigned int n)
{
for (unsigned int i = 0; i < n; ++i)
array[i] *= c;
}
void bar(float* restrict array, float* restrict array2, unsigned int m, unsigned int n)
{
for (unsigned int i = 0; i < m; ++i)
foo(&array[i * n], array2[i], n);
}
это нарушение правил ограничения в bar(), где вы передаете адрес части массива foo (), даже если вы никогда не используете псевдоним для части массива в bar ()?
2 ответов
(все цитаты относятся к N1256, который является C99 плюс технические исправления (TC3).)
формальное определение restrict
приведен в п. 6.7.3.1. Я цитирую самый важный подпункт ниже. P
- это restrict
- квалифицированный указатель на тип T
чья область действия является блоком B
. Выражение указателя E
считается на основе P
если это зависит от значения P
сам, а не значение, которое P
очки к.
во время каждого исполнения
B
, пустьL
быть любой lvalue, который имеет&L
на основе P. IfL
используется для доступа к значению объектаX
что он обозначает, иX
также изменено (любыми средствами), тогда применяются следующие требования:
T
не будет const-квалифицировано.- каждый другой lvalue используется для доступа к значению
X
также имеет свой адрес, основанные наP
.- каждый доступ, который изменяет
X
считается также изменитьP
для целей настоящего подпункта.- если
P
присваивается значение выражения указательE
это основано на другом ограниченном объекте указателяP2
, связанный с блокомB2
, тогда как исполнениеB2
должно начаться до исполненияB
, илиB2
заканчивается до назначения.если эти требования не выполнены, то поведение не определено.
давайте посмотрим, что правила должны сказать о доступе к частям bar
' s array
на 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), первое условие выполнено. Существует также один экземпляр третьего маркера (The доступ к b[i]
на foo
), что не является проблемой.
b
не ограничивать квалификацию. Согласно §6.3.2.3.2, " для любого классификатора q указатель на не-q-квалифицированный тип может быть преобразован в указатель на q-квалифицированная версия типа; значения, хранящиеся в исходном и преобразованном указателях, должны сравниваться равными". Поэтому преобразование из array+(i*n)
to 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).
нет, restrict означает, что массив не может ничего псевдонима, поэтому вы можете передавать материал в бар, не нарушая правил