При использовании ограничительного указателя в C, можно ли изменить переменную, используя ее начальный идентификатор? - PullRequest
4 голосов
/ 14 октября 2010

Когда используется указатель restrict в C, можно ли изменить переменную, используя ее начальный идентификатор ? Например:

int foo = 0;

int * restrict fooPtr = &foo;
++(*fooPtr);                   // Part 1: foo is 1 (OK)

++foo;                         // Part 2: foo is 2 (Is this OK?)

int * fooPtr2 = &foo;
++(*fooPtr2);                  // Part 3: foo is 3 (BAD: You shouldn't access via a second pointer)

... Я изменил значение от foo до foo после создания restrict fooPtr .

Часть 1 выглядит хорошо для меня. Я в замешательстве насчет Часть 2 . И из того, что я понимаю о restrict, Часть 3 , плохо (компилятор позволяет это, но его поведение не определено, и программист должен это не делать).

Ответы [ 4 ]

4 голосов
/ 14 октября 2010

Нет, часть 2 не в порядке.

Специфической частью стандарта является 6.7.3.1/4.Этот раздел довольно плотный и требует нескольких повторных чтений, но P - ограничительный указатель, а X - некоторый объект, к которому он используется, и который изменяется.Итак, в вашем примере P равно fooPtr, а X равно foo.Тогда:

Каждое другое значение lvalue, используемое для доступа к значению X, также должно иметь свой адрес на основе P.

"на основе" определено в предыдущем абзаце,Подводя итог, можно сказать, что lvalue foo не не имеет свой адрес, основанный на ограничивающем указателе P. Таким образом, правило нарушается при доступе к объекту foo через его собственное имя.* Часть 3 не в порядке по той же причине, lvalue *fooPtr2 также не основана на P, но также используется для доступа к X.

Я говорю «не в порядке» - если быть точным,комбинация 1 + 2 вызывает неопределенное поведение, как и комбинация 1 + 3.Пока вы на самом деле не обращаетесь к объекту через указатель restrict, ни одно из определений restrict не запускается.Если вы хотите, вы можете удалить часть 1, сохранить неиспользованный ограничительный указатель, и тогда 2 и 3 будут в порядке.

1 голос
/ 14 октября 2010

Предполагая, что все части вашего вопроса находятся в одном и том же блоке, неправильно обращаться к значению foo напрямую (часть 2) или к нему через другой указатель (часть 3):

  • 6.7.3.1 Формальное определение ограничения

У каждого другого lvalue, используемого для доступа к значению X, также должен быть свой адрес, основанный на P.

Формальное определение restrict довольно сложно следовать (по крайней мере, для меня), однако следующее менее формальное описание, также из стандарта, довольно неплохо суммирует его (по крайней мере, для этого случая):

Объект, доступ к которому осуществляется через ограниченный указатель, имеет специальную связь с этим указателем.Эта ассоциация, определенная в 6.7.3.1 ниже, требует, чтобы все обращения к этому объекту использовали, прямо или косвенно, значение этого конкретного указателя.

1 голос
/ 14 октября 2010

Я бы сказал, что № 2 - это плохо.Например, компилятор может оптимизировать загрузку значения в * fooPtr в регистр, а затем записать это значение регистра обратно в foo позже - после вашего ++ foo, так что ++ foo будет потеряно.

1 голос
/ 14 октября 2010

Да, «часть 3» - неопределенное поведение. Из спецификации C99 (6.7.3, пункт 7):

Объект, доступ к которому осуществляется через ограниченно-квалифицированный указатель имеет особая связь с этим указателем. Эта связь, определенная в 6.7.3.1 ниже, требует, чтобы все доступы к этот объект использовать, напрямую или косвенно, значение этого конкретный указатель.

...