Модифицирует ли объект указатель на постоянное неопределенное поведение? - PullRequest
4 голосов
/ 17 июня 2019

Предположим, у нас есть переменная int, обозначенная как const int*, которая в свою очередь имеет псевдоним int *.Из Стандарта ясно, является ли изменение переменной с помощью указателя int * неопределенным поведением или нет?

В качестве иллюстрации рассмотрим следующий код:

void increment(int* p) {
    (*p)++;
}

void call_increment(const int* p) {
    increment(p);
}

int main(void) {
    int x = 7;
    int* p = &x;

    call_increment(p);
}

Ответы [ 2 ]

8 голосов
/ 17 июня 2019

Изменение объекта с помощью указателя на const является некорректным, а не неопределенным поведением.
Исправление, которое путем отбрасывания const является корректным, если только объект, на который ссылаются, не является const.

В вашем коде другая проблема:
Вы отбрасываете const -квалификатор при передаче p из call_increment() в increment().

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

g++ -x c -std=c18 main.cpp && ./a.out
main.cpp: In function 'call_increment':
main.cpp:6:15: warning: passing argument 1 of 'increment' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
     increment(p);
               ^
main.cpp:1:21: note: expected 'int *' but argument is of type 'const int *'
 void increment(int* p) {
                ~~~~~^

Просто прислушайтесь к этому, и лучше попросите больше, по крайней мере -Wall -Wextra.

2 голосов
/ 17 июня 2019

Спецификатор const в C указывает, что lvalue не будет использоваться для изменения объекта, но обычно ничего не говорит о том, может ли объект быть изменен другими способами, в том числе с помощью неконстантного указателя, полученного из lvalue.

Два примечательных случая, когда это имеет значение для объекта:

  1. Если объявлено lvalue верхнего уровня const, но не volatile,не было бы определенных средств, с помощью которых его значение могло бы измениться, и Стандарт предлагал бы реализациям свободно предполагать, что это не будет.Если значение lvalue верхнего уровня равно const volatile, Стандарт не предвидит каких-либо конкретных средств, с помощью которых значение может измениться, но реализации качества, как правило, должны предусматривать возможность того, что значение может быть спонтанно изменено с помощью средств, о которых они ничего не знают.

  2. Если указатель на объект const квалифицирован restrict, любой объект, наблюдаемый через этот указатель или любое значение l, полученное из адреса, хранящегося в нем, должен иметь одно и то же значение в течение активного времени жизни.указателя.Таким образом, учитывая, например,

    int test(int const *restrict p)
    {
      if (*p == 1)
      {
        doSomething(p);
        return *p;
      }
      else
        return 1;
    }
    

    , компилятору будет разрешено генерировать код, который возвращает 1 без перезагрузки *p, но это не будет разрешено делать без квалификатора restrict.Это также верно, например, для

    int test(int const *p)
    {
      int const * restrict pp = p;
      if (*pp == 1) // Restrict-qualified pointer used to observe value
      {
        doSomething(pp);
        return *pp;
      }
      else
        return 1;
    }
    

    , но не

    int test(int const *p)
    {
      int const * restrict pp = p;
      if (*p == 1)
      {
        doSomething(pp);
        return *p;
      }
      else
        return 1;
    }
    

    Если копия p ранее была сохранена в глобальном объекте и doSomething должны были полностью игнорироватьсяpp, изменения в *p не повлияют на любой объект, доступ к которому осуществляется с помощью указателя, полученного из pp.

Если вы хотите максимизировать оптимизацию, указав, что объект, идентифицированный с помощью указателя, будетникогда не меняйте, обычно следует указывать указатель как restrict в дополнение к идентификации объекта как const.

...