Является ли это недопустимым использованием указателей 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. If L используется для доступа к значению объекта 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 был ограничен-квалифицированный, тогда мы должны были бы проверить четвертом пункте с Pb, Bfoo, P2array, B2bar. С 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 означает, что массив не может ничего псевдонима, поэтому вы можете передавать материал в бар, не нарушая правил