Является ли это недопустимым использованием указателей ограничения?

Предположим, у меня есть большой массив, в который я вычисляю индекс и передаю его второй функции. В качестве простого примера, что-то вроде:

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()?


person arsenm    schedule 04.10.2010    source источник


Ответы (2)


(Все ссылки относятся к 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, P2array, 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
comment
Я не могу сказать, говорите ли вы, что это правильно или неправильно, но поддерживает полное развертывание стандарта. - person Matt Joiner; 05.10.2010
comment
@Matt: код правильный (отчасти потому, что правила ограничения здесь мало что делают, а отчасти потому, что вы можете преобразовать квалификатор ограничения в подобласти, разделенной foo). - person Gilles 'SO- stop being evil'; 05.10.2010

Нет, ограничение означает, что массив не может использовать псевдонимы, поэтому вы можете передавать данные в панель, не нарушая правил.

person Spudd86    schedule 04.10.2010
comment
Я думаю, что этот вопрос требует цитирования из стандартных, а не случайных объяснений в отношении средств ограничения .... Правила псевдонимов C99 слишком сложны, чтобы полагаться на подобные случайные объяснения. - person R.. GitHub STOP HELPING ICE; 04.10.2010
comment
правила для ограничения довольно просты, до него нельзя добраться ничем, кроме указателя ограничения. - person Spudd86; 04.10.2010