Как компилятор будет вычислять * ptr , если ptr равен ptr для первого элемента статического массива?

#c #pointers #operators

#c #указатели #операторы

Вопрос:

Каков порядок вычисления в *ptr ? Меняется ли это, когда в операции задействованы указатели и значения lvalues?

Если приоритет a выше, чем *a или a , то почему *a сначала вычисляется возврат увеличенного значения с последующим изменением указателя, а не изменение указателя с последующим увеличением значения в местоположении. Ссылка на приоритет:https://en.cppreference.com/w/cpp/language/operator_precedence

 arr = {9, 99, 999 };
int *ptr = arr;
std::cout <<   *ptr   << 't';
std::cout << *ptr;

  

Я ожидал, что результат будет 100 100, но фактический результат был 10 99.

Комментарии:

1. Вместо бесконечного анализа нечитаемого кода, напишите читаемый код. <g>

Ответ №1:

Приращение постфикса a увеличивает указатель ptr, но возвращает копию ptr перед операцией (см. Разницу между префиксом / постфиксом). Таким образом, его можно переписать (как указано в ответе Квимби) как (*(ptr )) и выглядит следующим образом:

  1. ptr : увеличивает ptr так, чтобы он указывал на 99, но возвращает другой указатель, который по-прежнему указывает на 9
  2. * ptr : разыменования, вычисляется как 9
  3. *ptr : увеличивает значение, на которое указывает скопированный указатель, то есть увеличивает на 9 и возвращает 10

Здесь хорошо объясняется логика, лежащая в основе предварительного / последующего увеличения / уменьшения:

Операторы предварительного увеличения и предварительного уменьшения увеличивают или декрементируют значение объекта и возвращают ссылку на результат. Постинкрементное и пострекрементное уменьшение создает копию объекта, увеличивает или уменьшает значение объекта и возвращает копию, полученную до увеличения или уменьшения.

Из:https://en.cppreference.com/w/cpp/language/operator_incdec

Комментарии:

1. Итак, если бы мы скажем, перегрузили операторы предварительного и постинкрементного увеличения для класса, скажем, A, и создали конструктор копирования A, тогда приращение post выдало бы ошибку?

2. с чего бы это? Когда вы пишете оператор для пользовательского типа, он будет использоваться только при вашем вызове. Кстати, я рекомендую попробовать это самостоятельно, а не верить мне 🙂

Ответ №2:

Постфиксные операторы имеют более высокий приоритет, чем префикс, поэтому они более жестко привязываются / first so: *ptr совпадает с (*(ptr )) , который сводится к тому, какой операнд над чем работает. Таким образом, postfix будет применен к вашему указателю ‘ptr’, но ‘после’ первой строки std::cout . Префикс будет работать с разыменованным ptr, так что это все то же самое, что:

 int arr[] = {9, 99, 999 };
int *ptr = arr;
  (*ptr); // 9 1=10
std::cout << *ptr << 't';
ptr  ; // now ptr points to 99
std::cout << *ptr; 
  

Ответ №3:

Короче говоря, потому что *ptr переписывается как (*(ptr ))

Правила в ссылке довольно ясны:

  1. Постфикс имеет наивысший приоритет => *(ptr )
  2. Префиксы и * имеют одинаковый приоритет, и они являются правоассоциативными => (*(ptr ))

Выражение также можно разделить на отдельные операторы, подобные этому:

 arr = {9, 99, 999 };
int *ptr = arr;
int *ptr2 = ptr  ;//ptr2 still points to the first element
int val = *ptr2; // 9
int incVal=   val; // 10
  

Надеюсь, ясно, что ptr теперь указывает на второй элемент массива, а результатом выражения является увеличенное значение.

Ответ №4:

Поскольку вы используете одномерный массив

*ptr ссылается на приращение в указателе ptr к 0 элементам массива, после приращения результат для этого будет равен 10

Постфикс имеет наивысший приоритет => *(ptr )