Указатели с операторами увеличения и уменьшения - PullRequest
3 голосов
/ 16 мая 2019

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

Jdoodle onlinecompiler / C

Это должно выдать ошибку, однако успешная компиляция есть.

https://www.jdoodle.com/c-online-compiler

#include <stdio.h>

int main()
{
    int x = 30, *y, *z;

    y = &x; // Assume address of x is 1000 and integer is 4 byte size */
    z = y;
    *y++ = *z++;
    x++;
    return 0;
}

Ответы [ 4 ]

2 голосов
/ 16 мая 2019

В общем случае, когда оператор присваивания есть, левый операнд должен быть переменной, а не выражением ...

Левый операнд оператора присваивания является всегда выражением. Ограничение состоит в том, что это должно быть lvalue , которое (немного упрощает) выражение, обозначающее объект.

Простейшим случаем lvalue является имя объявленного объекта / переменной, например:

int n;
n = 42;

n - это выражение, в частности lvalue, которое обозначает объект с именем n.

В вашем примере у вас есть:

*y++ = *z++;

Приоритет оператора говорит, что оператор ++ связывается более тесно, чем оператор *, поэтому это эквивалентно:

*(y++) = *(z++);

y++ и z++ не являются значениями - это выражения, которые дают значения указателя, но они не обозначают никаких объектов. Но унарный оператор * дает вам lvalue, даже если его операнд не является lvalue. y++ возвращает значение указателя, а *(y++) - объект, на который указывает указатель.

Стандарт C не использует термин «переменная» в этом смысле. В нем говорится о объектах , которые могут быть или не быть объявлены объектами с простыми именами. an_object, *pointer_value, array[index] и structure_object.member относятся к объектам и могут отображаться слева от назначения.

2 голосов
/ 16 мая 2019

левый операнд должен быть переменной, а не выражением

Это недоразумение.Вы ищете термин lvalue .Это стандартный C-бессмысленный термин, происходящий от термина «левое значение».Определение термина lvalue приблизительно: выражение, обозначающее объект.

Правила для различных операторов, обсуждаемых здесь:

  • Левый операндиз операторов присваивания всегда должно быть lvalue.
  • Результат унарного оператора * определен как всегда lvalue, поэтому вы можете использовать его, как если бы он был объектом, и мы можем присвоитьЭто.
  • Результат работы операторов ++, однако, равен , а не lvalue, поэтому его нельзя использовать в качестве левого операнда присваивания.

Примеры:

int x; int* y;

x = 5;   // Here x is both an object and an lvalue, so the code is valid
y = &x;  // y is both a (pointer) object and an lvalue, code is valid.
*y = 0;  // *y is an lvalue designating the object y, code is valid
y++ = 1; // y++ is not an lvalue, the code is invalid

Что касается того, почему *y++ работает, это просто вопрос приоритета операторов.Сначала применяется ++, но поскольку это постфикс, изменение не происходит до конца выражения.Таким образом, * применяется к y, и результатом является lvalue.

Если вы написали ++*y = 0;, то ассоциативность оператора заставила * выполняться первым, результат *y был lvalue,И затем, когда вы используете ++ для этого lvalue, результат ++ не является lvalue, поэтому код недействителен.++*y сам по себе является допустимым кодом, но он не может быть левым операндом присваивания.

2 голосов
/ 16 мая 2019

Левый операнд присваивания не должен быть переменной.Например, следующие задания должны работать и работают отлично (и я полагаю, что вы это знаете и просто неправильно):

array[index] = value;
*ptr = value;

Я думаю, что вас смущает в *y++ = *z++; то, что вы думаете, что это назначениерезультат операции приращения, которая действительно не имеет смысла.Но это не приоритет этого выражения: *y++ эквивалентно *(y++), а не (*y)++.Таким образом, вы разыменовываете результат y++ и затем присваиваете значение этой разыменованной ячейке памяти, как если бы вы написали:

int *ptr = y++;
*ptr = *z++;
1 голос
/ 16 мая 2019

Предположим, что адрес x равен 1000 и sizeof(int) = 4 (для 32-bit компилятора), а затем при вычислении выражения:

 *y++=*z++;

Адрес z увеличивается

(Поскольку постинкремент (x ++) имеет более высокий приоритет, чем (* x))

, но до этого rvalue т.е. адрес, сохраняемый z назначен на указатель y.После этого происходит только приращение адреса и разыменование.

Аналогично случаю *y++, первый адрес назначается, происходит постинкремент, а затем разыменовывается.Если вы не уверены, что lvalue является переменной, нет необходимости, например, добавлять оператор, как в вашем коде, после инициализации указателя с действительным адресом

*y++;

также правильно и не помечает любую ошибку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...