Вопросы с ограниченным указателем

Я немного запутался в правилах, касающихся ограниченных указателей. Может быть, кто-то может мне помочь.

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

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

Спасибо! Эндрю

Ответы на вопрос(2)

restrict классификатор типа являетсяиндикация компилятору, если память адресованаrestrictквалифицированный указатель изменен, никакой другой указатель не получит доступ к той же памяти. Компилятор может решить оптимизировать код, включающийrestrictуказатели, которые в противном случае могут привести к некорректному поведению.Программист несет ответственность за то, чтобы указатели с ограниченным доступом использовались так, как они были предназначены для использования. В противном случае может возникнуть неопределенное поведение. (ссылка на сайт)

Как видно из приведенного выше описания, оба ваших назначения недопустимы, которые могут работать в исполняемых файлах, созданных одними компиляторами, но нарушаться в других. Не ожидайте, что сам компилятор выдаст ошибки или предупреждения какrestrict просто дает возможность выполнить определенную оптимизацию, которую он может не выполнять, как в случаеvolatile.

 Michael Burr27 сент. 2010 г., 06:12
Я не думаю, что компиляторы могут игнорироватьvolatile.
 einpoklum09 апр. 2013 г., 21:56
@R: Полагаю, вы ошибаетесь, поскольку реализация не может не поддерживать ввод-вывод с отображением в память - такой тип ввода-вывода просто существует независимо от компилятора ...
 R..27 сент. 2010 г., 06:23
Компиляторы, как правило, могут игнорироватьvolatile если реализация не поддерживает какие-либо асинхронные сигналы (и, следовательно, обработчики сигналов), которые могли бы изменить значение переменной вне нормального потока программы, или другие функциональные возможности, определенные реализацией (например, потоки или ввод-вывод в устройство отображения памяти), где volatile может сделать разница.
 supercat24 авг. 2015 г., 00:02
@einpoklum: Некоторые процессоры используют специальные инструкции (часто со своим выделенным адресным пространством) для целей ввода-вывода и не имеют ничего, кроме памяти, в какой-либо части адресного пространства, до которой могут добраться указатели. В таких инструкциях ввод-вывод обычно реализуется либо через встроенный компилятор, либо путем ссылки на код, написанный на другом языке. Например, большинство 8031 ​​вариантов имеют 128-байтовое адресное пространство для ввода / вывода, к которому можно получить доступ только через инструкции прямого режима; нет никаких инструкций, которые позволили бы писать код в регистр ввода / вывода, идентифицируемый указателем.
Решение Вопроса

restrict довольно запутанное определение классификатора (из C99 6.7.3.1 «Формальное определение ограничения»):

Пусть D будет объявлением обычного идентификатора, который обеспечивает средство для обозначения объекта P как ограниченного указателем типа T.

Если D появляется внутри блока и не имеет внешнего класса хранения, пусть B обозначает блок. Если D появляется в списке объявлений параметров определения функции, пусть B обозначает связанный блок. В противном случае пусть B обозначает блок main (или блок любой функции, вызываемой при запуске программы в автономной среде).

В последующем выражение указателя E называется основанным на объекте P, если (в некоторой точке последовательности при выполнении B до оценки E) модифицировать P так, чтобы он указывал на копию объекта массива, на который он ранее указывал изменит значение E. Обратите внимание, что «based» определяется только для выражений с типами указателей.

Во время каждого выполнения B пусть L будет любым значением l, имеющим & L на основе P. Если L используется для доступа к значению объекта X, который он обозначает, и X также изменяется (любым способом), то применяются следующие требования : T не должен быть постоянным. Каждое другое lvalue, используемое для доступа к значению X, должно также иметь свой адрес на основе P. Каждый доступ, который изменяет X, должен также рассматриваться как модифицирующий P, для целей этого подпункта. Если P присваивается значение выражения указателя E, которое основано на другом объекте ограниченного указателя P2, связанном с блоком B2, то либо выполнение B2 должно начинаться до выполнения B, либо выполнение B2 должно заканчиваться до назначение. Если эти требования не выполняются, то поведение не определено.

Здесь выполнение B означает ту часть выполнения программы, которая соответствует времени жизни объекта со скалярным типом и длительности автоматического хранения, связанной с B.

Мое прочтение вышеизложенного означает, что в вашем первом вопросеa не может быть назначенbдаже внутри «дочернего» блока - результат не определен. Такое назначение может быть сделано, еслиb были объявлены в этом «подблоке», но так какb объявлен в той же области, что иa, назначение не может быть сделано.

Для вопроса 2, задания междуc а такжеd также приводят к неопределенному поведению (в обоих случаях).

Соответствующий бит из стандарта (для обоих вопросов):

Если P присваивается значение выражения указателя E, которое основано на другом объекте ограниченного указателя P2, связанном с блоком B2, то либо выполнение B2 должно начинаться до выполнения B, либо выполнение B2 должно заканчиваться до назначение.

Поскольку ограниченные указатели связаны с одним и тем же блоком, блок B2 не может начинаться до выполнения B, или завершаться B2 до назначения (так как B и B2 являются одним и тем же блоком).

Стандарт дает пример, который делает это довольно ясно (я думаю - ясностьrestrict 4 коротких абзаца определения соответствуют правилам разрешения имен в C ++:

ПРИМЕР 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
    }
}

Ваш ответ на вопрос