При использовании ограничительного указателя в C, можно ли изменить переменную, используя ее начальный идентификатор?
При использовании 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 ответа
Нет, часть 2 не в порядке.
Особая часть стандарта - 6.7.3.1/4. Этот раздел довольно плотный и требует нескольких повторных чтений, но P - ограничительный указатель, а X - некоторый объект, к которому он используется, и который изменяется. Итак, в вашем примере P fooPtr
и Х это foo
, Затем:
Каждое другое значение lvalue, используемое для доступа к значению X, также должно иметь свой адрес на основе P.
"на основе" определяется в предыдущем абзаце, и в итоге, lvalue foo
не имеет своего адреса на основе ограничительного указателя P. Таким образом, правило нарушается при доступе к объекту foo
через собственное имя.
Часть 3 не в порядке по той же причине, lvalue *fooPtr2
также не основан на P, но также используется для доступа к X.
Я говорю "не в порядке" - если быть точным, комбинация 1+2 вызывает неопределенное поведение, как и комбинация 1+3. Пока вы на самом деле не обращаетесь к объекту через указатель restrict, ни одно из определений restrict не запускается. Если вы хотите, вы можете удалить часть 1, сохранить неиспользуемый ограничительный указатель, и тогда 2 и 3 будут в порядке.
Предполагая, что все части вашего вопроса находятся в одном и том же блоке, получить доступ к значению foo
напрямую (часть 2) или доступ к нему через другой указатель (часть 3):
- 6.7.3.1 Формальное определение ограничения
Каждое другое значение lvalue, используемое для доступа к значению X, также должно иметь свой адрес на основе P.
Формальное определение restrict
довольно трудно следовать (по крайней мере, для меня), однако следующее менее формальное описание также из стандарта довольно неплохо суммирует его (по крайней мере, для этого случая):
Объект, доступ к которому осуществляется через ограниченный указатель, имеет специальную связь с этим указателем. Эта связь, определенная в 6.7.3.1 ниже, требует, чтобы все обращения к этому объекту использовали, прямо или косвенно, значение этого конкретного указателя.
Я бы сказал, что № 2 плохо. Например, компилятор может оптимизировать загрузку значения в *fooPtr в регистр, а затем записать значение этого регистра обратно в foo позже - после вашего ++foo, так что ++ foo будет потерян.
Да, "часть 3" - неопределенное поведение. Из спецификации C99 (6.7.3, пункт 7):
Объект, к которому осуществляется доступ через ограниченно-квалифицированный указатель, имеет специальную связь с этим указателем. Эта связь, определенная в 6.7.3.1 ниже, требует, чтобы все обращения к этому объекту использовали, прямо или косвенно, значение этого конкретного указателя.