Я немного смущен правилами относительно ограниченных указателей. Может быть, кто-то там может помочь мне.
-
Является ли законным определять вложенные ограниченные указатели следующим образом:
int* restrict a;
int* restrict b;
a = malloc(sizeof(int));
// b = a; <-- assignment here is illegal, needs to happen in child block
// *b = rand();
while(1)
{
b = a; // Is this legal? Assuming 'b' is not modified outside the while() block
*b = rand();
}
-
Можно ли получить ограниченное значение указателя следующим образом:
int* restrict c;
int* restrict d;
c = malloc(sizeof(int*)*101);
d = c;
for(int i = 0; i < 100; i++)
{
*d = i;
d++;
}
c = d; // c is now set to the 101 element, is this legal assuming d isn't accessed?
*c = rand();
Спасибо!
Эндрю
Ответ 1
Для справки здесь определение restrict
довольно сложное определение (из C99 6.7.3.1 "Формальное определение ограничения" ):
Пусть D - объявление обычного идентификатор, который предоставляет средства обозначение объекта P как ограничивающий указатель на тип T.
Если D появляется внутри блока и не имеет класса хранения extern, пусть B обозначает блок. Если D появляется в списке параметров объявления функции определим, пусть В обозначает связанный блок. В противном случае пусть B обозначают блок основного (или блока любой функции вызывается при запуск программы в автономном режиме среда).
В дальнейшем указатель выражение E, как говорят, основано на объект P if (в некоторой точке последовательности в исполнении B до оценка E) изменение P на точку к копии объекта массива в который он ранее указывал, изменился бы значение E. Заметим, что "основанный" определяется только для выражений с типы указателей.
Во время каждого исполнения B пусть L любое lvalue, которое имеет & L на основании P. If L используется для доступа к значению объект X, который он обозначает, и X также измененными (любыми способами), то применяются следующие требования: T не быть const-квалифицированным. Каждый второй lvalue используется для доступа к значению X также имеет свой адрес, основанный на P. Каждый доступ, который изменяет X, должен следует рассматривать также для изменения P, для цели этого подпункта. Если P присваивается значение указателя выражение E, основанное на другом ограниченный объект-указатель P2, связанных с блоком B2, то либо исполнение B2 должно начаться раньше исполнение B или исполнение B2 заканчивается до назначение. Если эти требования не выполняются, то поведение undefined.
Здесь выполнение B означает, что часть выполнения программы, которая соответствовала бы время жизни объекта со скалярным типом и продолжительность автоматического хранения связанных с B.
Мое прочтение выше означает, что в вашем первом вопросе a
нельзя назначить b
, даже внутри "дочернего" блока - результат undefined. Такое назначение может быть выполнено, если b
было объявлено в этом "подблоке", но поскольку b
объявляется в той же области, что и a
, назначение не может быть выполнено.
Для вопроса 2 назначения между c
и d
также приводят к поведению undefined (в обоих случаях).
Соответствующий бит из стандарта (для обоих вопросов):
Если P присвоено значение a выражение указателя E, основанное на другой ограниченный объект-указатель P2, связанных с блоком B2, то либо исполнение B2 должно начаться раньше исполнение B или исполнение B2 заканчивается до назначение.
Так как ограниченные указатели связаны с одним и тем же блоком, невозможно, чтобы блок B2 начинался до исполнения B, или для B2 до конца назначения (поскольку B и B2 являются одним и тем же блоком).
Стандарт дает пример, который делает это довольно ясным (я думаю - четкость определения restrict
4 коротких параграфа совпадает с правилами разрешения имен С++):
ПРИМЕР 4:
Правила, ограничивающие назначение между ограниченные указатели не различать вызов функции и эквивалентный вложенный блок. За одним исключением, только "внешние-внутренние" задания между ограниченные указатели, объявленные во вложенных блоки определили поведение.
{
int * restrict p1;
int * restrict q1;
p1 = q1; // undefined behavior
{
int * restrict p2 = p1; // valid
int * restrict q2 = q1; // valid
p1 = q2; // undefined behavior
p2 = q2; // undefined behavior
}
}
Ответ 2
Квалификатор типа restrict
является индикатором для компилятора, который, если память, адресованная restrict
-qualified pointer, изменена, никакой другой указатель не получит доступ к той же самой памяти. Компилятор может выбрать оптимизацию кода с restrict
-qualified указателями таким образом, который мог бы привести к неправильному поведению. Ответственность программиста состоит в том, чтобы обеспечить использование указателей с ограничениями, поскольку они предназначены для использования. В противном случае может возникнуть поведение undefined. (ссылка)
Как видно из приведенного выше описания, оба назначения являются незаконными, что может работать в исполняемых файлах, созданных некоторыми компиляторами, но ломаться в других. Не ожидайте, что сам компилятор испустит ошибки или предупреждения, поскольку restrict
просто дает возможность выполнить определенную оптимизацию, которую он может выбрать не выполнять, например, в случае volatile
.