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

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

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

    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.