Это недопустимое использование ограничительных указателей? - PullRequest
6 голосов
/ 04 октября 2010

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

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);
}

Это нарушает правила restrict в bar (), где вы передаете адрес части массива в foo (), даже если вы никогда не используете псевдоним для части массива в bar ()? *

Ответы [ 2 ]

9 голосов
/ 04 октября 2010

(все ссылки относятся к 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 должно закончиться доназначение.

Если эти требования не выполнены, то поведение не определено.


Давайте посмотрим, что правила должны сказать о доступе к деталямbar 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), первое условие выполняется.Существует также один экземпляр третьей маркированной точки (доступ к 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);
}

ADDED : для решения вашей конкретной проблемы «в bar (), где вы проходитеадрес части массива для foo () », это не проблема: restrict применяется к указателю, а не к массиву, и вы можете выполнить арифметику с ним (пункт 2).

0 голосов
/ 04 октября 2010

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

...