Когда выражение указателя «основано» на другом указателе? - PullRequest
0 голосов
/ 26 февраля 2019

В Раздел 6.7.3.1 стандарта языка C относительно restrict гласит:

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

  2. ...

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

Я не понимаю, что это говорит - буквально:

  • Кто сказал, что P указывает на «копиюобъект массива "?
  • Почему P" ранее "указывал на что-либо?То есть кто сказал, что мы изменили его значение?
  • Предположим, E - указатель локальной области видимости.Зачем модифицировать любое выражение указателя, кроме самого указателя E, «изменить значение E»?Это может изменить значение, на которое указывает E.Правильно?

Может ли кто-нибудь помочь мне интерпретировать этот фрагмент текста, чтобы придать ему больше смысла?

(Вдохновленный этим ответом )

Ответы [ 4 ]

0 голосов
/ 27 февраля 2019

Точка 3 может быть выражена в коде как (примерно):

#define E(P)  ( (P) + 1 )   // put the expression you want to test here

extern T obj;    // T is some type
T copy = obj;

if ( E(&obj) != E(&copy) )
    printf("the expression in the macro is based on P")

Формальное определение языка, используемое в стандарте, позволяет E быть недетерминированным и другими патологическими случаями (например, * 1005).*), а мой пример - нет.

Стандартная версия состоит в том, что мы сравниваем, какой будет результат E(&obj) с результатом E(&copy) в том же контексте.

0 голосов
/ 27 февраля 2019

Определение «на основе» предназначено для определения транзитивного отношения между указателями, но его фактическая формулировка приведет к неосуществимому определению, которое, насколько я могу судить, не соответствует ни одному действительному поведению компилятора.

Было бы проще транзитивно применить следующее правило (и именно это, похоже, и делают компиляторы): если *p является указателем типа T*, следующие указатели «основаны» на p:

  • p+(intExpr) или p-(intExpr)
  • (otherType*)p
  • &*p
  • &p->structMemberofNonArrayType или &p->unionMemberofNonArrayType
  • p->structMemberofArrayType или p->unionMemberofArrayType
  • &p[intExpr]
  • Любой указатель, основанный на любом из вышеперечисленных

Я не думаю, что Стандарт действительно четко(someType*)someIntegerFunction((uintptr_t)p) и я не думаю, что авторы компилятора также ясны.

Обратите внимание, что любое q, полученное из p через любое из вышеприведенных выражений, кроме одного, включающего приведение к uintptr_t, разницамежду (char*)p и (char*)q не будет зависеть от адреса p.

Incidentally, вот пример проблемного углового случая:

int test1(int * restrict p1, int * restrict p2, int n)
{
    int *restrict p3 = p1+n;
    // How would p4 and p5 be affected if p3 were replaced
    // with a pointer to a copy here?
    int *p4 = p3;
    if (p3 != p1) p4=p1;
    int *p5 = p2 + (p3 == p1);
    *p3 = 1;
    *p5 = 2;
    return *p4;
}

Используя транзитивные способы формирования указателя на основе другого, если n равен нулю, p4 будет явно основываться на p3,Однако указатель p5 не будет получаться из p3, поскольку отсутствует последовательность «основанных на» шагов, по которым можно было бы получить его значение.

Попытка применить правила, приведенные в Стандарте, кСлучай n==0 путем замены p3 указателем на копию массива не повлияет на значение p4, но повлияет на значение p5.Это означало бы, что p4 не основано на p3, но p5 так или иначе.

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

0 голосов
/ 27 февраля 2019

Прочитав несколько комментариев, а также ответ @ EricPostpischil, я попытался обобщить формулировку, которая, на мой взгляд, будет более четкой, хотя и немного более длинной, для уточнения вещей и ответа на поставленные вопросы.

Оригиналтекст:

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

Уточненный текст:

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

  • Тривиальное ограничение здравомыслия: модификация P должна происходить в точке последовательности .
  • P может быть изменена только для указания на идентичныйкопия того, на что он указывал первоначально.
    (И поскольку в целом мы можем думать, что указатели всегда указывают на объект массива - P можно установить только для указания на копию этого объекта массива).
0 голосов
/ 26 февраля 2019

Кто сказал, что P указывает на «копию объекта массива»?

Арифметика указателей определяется (в C 2018 6.5.6 8 и 9) в терминах указателей наэлементы массива.Для этой цели отдельный объект обрабатывается как массив из одного элемента.Таким образом, всякий раз, когда у нас есть какой-либо ненулевой указатель на объект, в этой модели он указывает на массив.

Почему P «ранее» указывал на что-либо?То есть кто сказал, что мы изменили его значение?

В цитируемом вами тексте написано: «Чтобы выяснить, основан ли E на P, давайте гипотетически сделаем копию массивачто P указывает, а затем присваивает P указатель на соответствующее место в копии. ”Таким образом, в цитируемом вами тексте говорится, что мы меняем значение P, а затем мы сравниваем значениеE с этим изменением и без него.

Предположим, E является указателем локальной области видимости.Зачем модифицировать любое выражение указателя, кроме самого указателя E, «изменить значение E»?Это может изменить значение, на которое указывает E.Правильно?

Объекты и значения не имеют области видимости.Идентификаторы имеют область применения.Но давайте рассмотрим идентификатор с областью действия блока:

// P is a pointer into A.
// S is the size of A.
// A is the start of an array not contained in any other array.
void foo(char *P, size_t S, char *A)
{
    void *E = P+2;
}

Для иллюстрации предположим, что P имеет значение 0x1004, а A равно 0x1000.E основано на P?Ну, учитывая вышеизложенное, E - это 0x1006.Предположим, что мы рассматриваем этот код перед определением E:

    char *N = malloc(S);
    memcpy(N, A, S);
    P = P - A + N;

Предположим, malloc возвращает 0x2000.Каким будет значение E?Это будет 0x2006.Это отличается от 0x1006.Поэтому E основан на P.

С другой стороны, учтите следующее:

void foo(char **P, size_t S, char **A)
{
    #if OnOrOff
        char *N = malloc(S);
        memcpy(N, A, S);
        P = P - A + N;
    #endif
    char **E = P[3];
}

Теперь значение E изменится в зависимости от того, OnOrOffэто правда или ложь?Нет, в любом случае он получит значение, являющееся ссылочным элементом A, напрямую или из копии.Тот факт, что P может указывать на A или N, не влияет на значение E.Так что это E не основано на P.

...