Операции с указателями и приоритет операторов в C

Фон

Только что сегодня разговаривал с парнем C, и мы не согласились со следующим:

int intgA[2] = { 1, 2 };
int intgB[2] = { 3, 5 };

int *intAPtr = intgA;
int *intBPtr = intgB;

Итак, когда мы делаем:

*intAPtr++ = *intBPtr++;

Мой анализ

Первый:

intBPtr увеличивается на единицу, теперь указывая на адрес 5. Затем, уважение, сохраняя значение 5;

intAPtr также увеличивается на единицу, теперь указывая на адрес 2. Впоследствии ссылаясь и значение равно 2;

Наконец:

2 заменено на 5.

Соответственно они равны: 5 и 5.

Его анализ

Значение *intBPtr сначала присваивается *intAPtr.

Следовательно, они становятся: 3 и 3.

Затем и *intAPtr, и *intBPtr увеличиваются на единицу.

Итак, соответственно они становятся: 4 и 4.

Мое предположение

Я думал, что оператор ++ имеет приоритет как над *, так и над =, отсюда и мое утверждение.

Например, если бы у нас было:

*intAPtr++; 

Результат должен быть 2, верно? Потому что мы сначала увеличиваем указатель, а потом разыменовываем.

Так почему же в приведенном выше случае, как он утверждает, мы сначала присваиваем значение intBPtr значению intAPtr и увеличиваем значения в последнюю очередь?

Приняв во внимание все предложения, я запустил код в среде IDE, и результат подтвердил результат @sujin:

Хотя это подтверждает, что я был прав хотя бы в плане приоритета:

Это: *intAPtr++ = *intBPtr++;

intAPtr++ имеет более высокий приоритет, что приводит к: intAPtr увеличивает свой адрес на 1.

Теперь указывая на: адрес 2.

А так же:

intBPtr++ также увеличивается на 1 (адрес).

Теперь указывая на: адрес 5.

Затем наступает очередь *:

Таким образом, оба разыменовываются (*) соответственно 2 и 5.

Но проблема по-прежнему существует, потому что указанное выше назначение (=), похоже, не имело места.

Если бы это было так, то оба стали бы 5.

С нетерпением жду дальнейших разъяснений.


person Unheilig    schedule 06.01.2014    source источник
comment
intAPtr++ — постинкремент. Весьма вероятно, что операции приращения выполняются последними после присваивания. Пробовали ли вы поместить этот код в IDE и запустить его, чтобы посмотреть, что он делает?   -  person Robert Harvey    schedule 06.01.2014
comment
Не имеет значения, когда происходит приращение указателя по отношению к присваиванию. Возвращаемое значение постинкремента — это новый временный объект, отдельный от указателя, который увеличивается.   -  person Cubbi    schedule 06.01.2014
comment
@Cubbi: Чтобы проверить поведение, просто запустив код, потребуется 5 минут.   -  person Robert Harvey    schedule 06.01.2014


Ответы (6)


Заявление

*intAPtr++ = *intBPtr++;

анализируется как

*(intAPtr++) = *(intBPtr++);

и распадается следующим образом:

  • Значение, на которое в данный момент указывает intBPtr (3), присваивается местоположению, на которое указывает intAPtr (intgA[0]);
  • Указатели intAPtr и intBPtr увеличиваются.

Точный порядок, в котором это происходит, не указан; вы не можете полагаться на то, что intBPtr увеличивается после intAPtr или наоборот, и вы не можете полагаться на присваивание, происходящее до увеличения, и т. д.

Так что к тому времени, когда все это будет сделано, intgA[0] == 3 и intAPtr == &intgA[1] и intBPtr == &intgB[1].

Выражение a++ оценивается как значение a перед приращением.

person John Bode    schedule 06.01.2014
comment
+1 за The exact order in which these things happen is unspecified. - person haccks; 06.01.2014

Вы и другой парень оба не правы!

Либо
1. сначала увеличиваются оба указателя, а затем происходит присваивание, либо
2. увеличивается один указатель, затем происходит присваивание, затем увеличивается другой указатель, либо
3. сначала происходит присваивание, а затем увеличивается указатель .

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

C-faq: 3.2:

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

Прочитайте этот ответ, данный Eric Lippert за подробными разъяснениями.

person haccks    schedule 06.01.2014
comment
Спасибо за крик. Я отмечаю, что вы упустили случай из вашего второго заявления. ... или увеличивается один указатель, затем происходит присваивание, затем увеличивается другой указатель. - person Eric Lippert; 07.01.2014
comment
@ЭрикЛипперт; Спасибо, что указали на это. Добавил этот случай в ответ. :) - person haccks; 07.01.2014

Да, ++ связывает крепче, чем *, но вы неправильно поняли, как это работает. var++ увеличивает var, но оценивается как значение var перед приращением. Вы можете думать об этом как о синтаксическом сахаре для (var += 1, var - 1), если хотите.

Другой способ думать об этом таков: если бы это работало так, как вы думали, тогда не было бы никакой разницы между var++ и ++var, и ни один из них не был бы включен в язык в первую очередь. C почти не имеет избыточности.

(Приоритет привязки имеет значение; например, это означает, что *var++ увеличивает значение переменной var, тогда как (*var)++ увеличивает значение в ячейке памяти по адресу *var.)

person zwol    schedule 06.01.2014
comment
Коллега также не прав, потому что ++ относится к указателям, а не к значениям, как он, кажется, утверждает... - person glglgl; 06.01.2014
comment
@glglgl Дох, я неправильно понял вопрос. Думаю, я просто удалю это заявление, а не попытаюсь объяснить его более подробно. - person zwol; 06.01.2014
comment
@hackks Нет. var не обязательно является ячейкой памяти (она может существовать только в регистре), но *var определенно является ячейкой памяти. Я вижу, что то, что я написал, могло бы быть яснее, хотя. - person zwol; 07.01.2014
comment
var объявлен как указатель. Я думаю, что в случае *var++ сначала происходит var++ (приоритет), который увеличивает ячейку памяти, а затем имеет место *, который разыменовывает его до его значения. Если бы var было объявлено как переменная, var++, конечно, означало бы увеличение значения var, как вы утверждали, что здесь не так, потому что var объявлено как указатель. - person Unheilig; 07.01.2014
comment
@Unheilig Нет, вы цепляетесь за свое первоначальное заблуждение. ++ в *var++ увеличивает var (а не место в памяти *var) из-за приоритета, но оценивает значение var до того, как произошло увеличение, что означает, что значением общего выражения является значение в ячейке памяти, исходно на которую указывает var. Посмотрите еще раз на то, что я написал о синтаксическом сахаре. - person zwol; 07.01.2014
comment
@Unheilig Кроме того, то, что var является указателем, не означает, что var не является переменной. В исходном коде intAPtr и intBPtr определенно являются и переменными, и указателями. - person zwol; 07.01.2014

Основной эффект оператора ++ состоит в том, чтобы произвести значение своего операнда (без его увеличения). Побочный эффект заключается в увеличении операнда.

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

В *x++, который анализируется как *(x++), оператор * применяется к значению, созданному основным эффектом, поэтому операция * такая же, как если бы вычислялся *x. Инкремент происходит «на стороне», вне основного вычисления выражения, поэтому он не принимает участия в определении значения *x++.

person Eric Postpischil    schedule 06.01.2014

Пример кода (в Linux):

#include <stdio.h>
#include <stdlib.h>
int main() {
    int intgA[2] = { 1, 2 };
    int intgB[2] = { 3, 5 };
    int *intAPtr = intgA; //1
    int *intBPtr = intgB; //3
    *intAPtr++ = *intBPtr++;
    // *intAPtr = *intBPtr;
    // *intAPtr++ = 25; 
    printf("op: %d %d\n", *intAPtr, *intBPtr);

   return 0;
}

выход:

op: 2 5

сначала он присваивает intBPtr intAPtr, затем происходит приращение, потому что это пост-приращение.

person sujin    schedule 06.01.2014
comment
сначала он присваивает intBPtr intAPtr, затем происходит приращение.: как вы могли такое сказать? - person haccks; 06.01.2014

Я согласен с ответом Джона Боде, чтобы немного упростить его, я просто напишу его в коде:

   *intAptr = * intBPtr;

равно:

   *intAptr =*intBPtr 
   intAPtr+=1;
   inrBPtr+=1;

Итак, что мы получаем: intAPtr указывает на 2 intBPtr указывает на 5

person Roma-MT    schedule 09.01.2014