Вопросы с ограниченным указателем - PullRequest
5 голосов
/ 27 сентября 2010

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

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

    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();
    }
    
  2. Допустимо ли выводить значение ограниченного указателя следующим образом:

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

Спасибо! Andrew

Ответы [ 2 ]

4 голосов
/ 27 сентября 2010

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

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

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

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

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

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

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

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

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

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

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

В стандарте приведен пример, который проясняет это (я думаю - четкость 4 коротких параграфов определения restrict соответствует нормам разрешения имен в 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
    }
}
1 голос
/ 27 сентября 2010

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

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

...