Вопрос

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

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

Спасибо! Андрей

Это было полезно?

Решение

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

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

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

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

Во время каждого исполнения B позвольте L быть любым Lvalue, который имеет и 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
    }
}

Другие советы

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top