Операции с указатели и приоритет на оператори в 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
Без значение е кога нарастването на показалеца се извършва по отношение на присвояването. Върнатата стойност на post-increment е нов временен обект, отделен от указателя, който се увеличава.   -  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:

Не е гарантирано, че увеличение или намаление се извършва веднага след отказ от предишната стойност и преди да бъде оценена която и да е друга част от израза. Просто се гарантира, че актуализацията ще бъде извършена някъде преди изразът да се счита за „завършен“.

Прочетете този отговор, даден от Ерик Липерт за подробно обяснение.

person haccks    schedule 06.01.2014
comment
Благодаря за поздрава. Отбелязвам, че сте пропуснали случай във второто си изявление. ... или единият указател се увеличава, тогава се извършва присвояването, след което другият указател се увеличава. - person Eric Lippert; 07.01.2014
comment
@EricLippert; Благодаря, че го посочи. Добавен е този случай в отговора. :) - 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
@hackcks No. 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