Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1] 2  Все   Вниз
  Печать  
Автор Тема: порядок выполнения постфикса и префикса  (Прочитано 37719 раз)
0 Пользователей и 4 Гостей смотрят эту тему.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« : 02-10-2003 02:16 » new

:arrow: если написать так
Код:
for(int i=0;  i<N;  a1[i++]=a2[i]);
или
Код:
for(int i=0;  i<N;  a1[i]=a2[i++]);
массив исправно копируется в обоих случаях, тогда скажите,
когда же выполняется постфикс - после ВСЕГО выражения что-ли?

 :arrow: что будет если
Код:
1)   a1[i++]=a2[i++];
2)   a1[++i]=a2[++i];
3)   a1[++i]=a2[i++];
« Последнее редактирование: 20-11-2007 16:19 от Алексей1153++ » Записан

NetRaider
Гость
« Ответ #1 : 02-10-2003 04:17 » 

Здесь дело не в приоритетах и порядке выполнения постфикса и префикса, а в sequence point(точка следования). Тип операции (постфиксная/префиксная) влияет только на то, какое значение будет использовано в качестве результата подвыражения i++ (или ++i). В выражениях

a1[i++]=a2[i++];
a1[++i]=a2[++i];
a1[++i]=a2[i++];

отсутствует промежуточная точка следования между двумя модификациями одного скалярного объекта(но точка следования есть в конце полного выражения). Поэтому инкремент/декремент может выполниться и до вычисления выражения(всего), и после вычисления выражения и в середине вычисления выражения. Имеем undefined behavior. В выражение a1=a2[i++] аналогично.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #2 : 02-10-2003 05:53 » 

то есть если i в выражении используется более одного раза, то лучше
Код:
a1[i]=a2[i];
i++;
:?:
« Последнее редактирование: 20-11-2007 16:21 от Алексей1153++ » Записан

Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #3 : 02-10-2003 08:22 » 

Привет, Лёха, нет тут "лучше-хуже". В зависимости от того, что тебе надо используешь пост- или префикс. Ключевая фраза у NetRaider:

Цитата: NetRaider
Тип операции (постфиксная/префиксная) влияет только на то, какое значение будет использовано в качестве результата подвыражения i++ (или ++i).

и

Цитата: NetRaider
точка следования между двумя модификациями


в
for(int i=0; i<N; a1[i++]=a2[i]);

на первом шаге у тебя в a1[i++] i=0, после i++ a2[i] получает тоже 0 в качестве индекса. Те a1[0]=a2[0]. И только при след шаге (если у тебя нет доп. изменений счётчика) в a1[i++] i станет равной 1.
Тоже самое будет и для:
for(int i=0; i<N; a1[i]=a2[i++]);

Если же ты используешь  
for(int i=0; i<N; a1[++i]=a2[i]);

то уже на первом шаге i=1 в обоих частях присваивания a1[1]=a2[1]
и тд.

А чтоб увидеть влияние пост - префикса попробуй так:

for(int i=0; ++i<N; a1[i]=a2[i]);
и
for(int i=0; i++<N; a1[i]=a2[i]);

посмотри как инициаллизируются массивы и значение i после цикла.
« Последнее редактирование: 20-11-2007 16:23 от Алексей1153++ » Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
Anonymous
Гость
« Ответ #4 : 02-10-2003 09:36 » 


привет!

попробую. скорее всего в
for(int i=0; ++i<N; a1[i]=a2[i]);

нулевой элемент не бует скопирован, в отличие от
or(int i=0; i++<N; a1[i]=a2[i]);



но вопрос-то у меня был маненько о другом -- о сочетании -фиксов в выражении, например

a1[++i]=a2[i++]; или
a1[(++i)  + ( i++)]=a2[i++];

_______
1153
« Последнее редактирование: 20-11-2007 16:24 от Алексей1153++ » Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #5 : 02-10-2003 09:45 » 

Вот, если интересно, cледственный эксперимент с gcc.
Код:
int main() {
    int i,a[10],b[10];

    i=0;
    a[i++]=b[i++];
    a[++i]=b[i++];
    a[++i]=b[++i];
    return i;
    }
Напоминаю - синтаксис AT&T читается так: "movl %esp,%ebp" соотв. интеловсому "mov ebp,esp" , а "leal   0(,%eax,4), %edx" соотв. "lea edx,[eax*4+0]".
Компилил без оптимизации, коментарии мои.
Чтобы понять что к чему, необязательно разбираться детально - достаточно посмотреть место расположения команд "incl (%eax)" в блоке, описывающем одно выражение.
Код:
main:
pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $116, %esp
; i=0
movl $0, -12(%ebp)
; a[i++]=b[i++]
movl -12(%ebp), %eax
movl %eax, %eax
leal 0(,%eax,4), %ebx
leal -72(%ebp), %eax
movl %eax, %ecx
movl -12(%ebp), %eax
movl %eax, %eax
leal 0(,%eax,4), %edx
leal -120(%ebp), %eax
movl %eax, %eax
movl (%edx,%eax), %eax
movl %eax, (%ebx,%ecx)
leal -12(%ebp), %eax
incl (%eax) ; <<<<<<<
leal -12(%ebp), %eax
incl (%eax) ; <<<<<<<
; a[++i]=b[i++]
leal -12(%ebp), %eax
incl (%eax) ; <<<<<<<
movl -12(%ebp), %eax
movl %eax, %eax
leal 0(,%eax,4), %ebx
leal -72(%ebp), %eax
movl %eax, %ecx
movl -12(%ebp), %eax
movl %eax, %eax
leal 0(,%eax,4), %edx
leal -120(%ebp), %eax
movl %eax, %eax
movl (%edx,%eax), %eax
movl %eax, (%ebx,%ecx)
leal -12(%ebp), %eax
incl (%eax) ; <<<<<<<
; a[++i]=b[++i]
leal -12(%ebp), %eax
incl (%eax) ; <<<<<<<
movl -12(%ebp), %eax
movl %eax, %eax
leal 0(,%eax,4), %ebx
leal -72(%ebp), %eax
movl %eax, %ecx
leal -12(%ebp), %eax
incl (%eax) ; <<<<<<<
movl -12(%ebp), %eax
movl %eax, %eax
leal 0(,%eax,4), %edx
leal -120(%ebp), %eax
movl %eax, %eax
movl (%edx,%eax), %eax
movl %eax, (%ebx,%ecx)
; return i
movl -12(%ebp), %eax
movl %eax, %eax
movl -4(%ebp), %ebx
leave
ret

Так что это код будет соответствовать след.:
a[0]=b[1];
a[3]=b[3];
a[5]=b[6];
i=6;

Что еще забавно, если включить оптимизацию, то полученый ассемблерный код будет иметь С-эквивалент:
int main() { return 6; }
« Последнее редактирование: 20-11-2007 16:26 от Алексей1153++ » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #6 : 02-10-2003 10:27 » 

то есть - засада у зоосада...


Цитата

int main() { return 6; }


-- а куда остальное делось? Ага    массивы? Ага  Ага
Записан

RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 02-10-2003 11:38 » 

Алексей1153
Это оптимизатор понял что массивы бестолковые и убрал их  Отлично
Вот это получается с ключом -O1
Код:
main:
pushl %ebp
movl %esp, %ebp
movl $6, %eax
subl $104, %esp
leave
ret
Только не понятно зачем стеком крутил...

Касательно темы, итог: операция постфиксного/префиксного инкремента/декремента выполняется как и все "нормальные" операции - что написано, то и выполняется и никакого скрытого контекста нет, есть только приоритеты.
« Последнее редактирование: 20-11-2007 16:35 от Алексей1153++ » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #8 : 02-10-2003 15:28 » 

Цитата

Только не понятно зачем стеком крутил...

при возврате будет какая-то заморочка, ему виднее...
Записан

NetRaider
Гость
« Ответ #9 : 06-10-2003 09:00 » 

Цитата

то есть если i в выражении используется более одного раза, то лучше

a1=a2;
i++;

Да

Цитата

но вопрос-то у меня был маненько о другом -- о сочетании -фиксов в выражении, например

a1[++i]=a2[i++]; или
a1[(++i) + ( i++)]=a2[i++];

 
Здесь будет undefined behavior. Во второй строке продвинутый компилятор может превратить два вызова i++ в один.  Повторюсь - инкремент/декремент может выполниться и до вычисления выражения(всего), и после вычисления выражения и в середине вычисления выражения.

Цитата

for(int i=0; i<N; a1[i++]=a2);

на первом шаге у тебя в a1[i++] i=0, после i++ a2 получает тоже 0 в качестве индекса. Те a1[0]=a2[0]. И только при след шаге (если у тебя нет доп. изменений счётчика) в a1[i++] i станет равной 1.


Почему не наоборот ? (о порядке операций)

Цитата

Касательно темы, итог: операция постфиксного/префиксного инкремента/декремента выполняется как и все "нормальные" операции - что написано, то и выполняется и никакого скрытого контекста нет, есть только приоритеты.


Верно только  в том случае, если переменная между двумя точками следования изменялась не более одного раза.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #10 : 06-10-2003 09:41 » 

NetRaider, проанализировал еще раз ассемблерный код (наверху).
Получается так (выражение и эквивалент):
Код:
a[i++]=b[i++]; // a[i]=b[i]; i+=2;
a[++i]=b[i++]; // i+=1; a[i]=b[i]; i+=1;
a[++i]=b[++i]; // i+=1; a[i]=b[i+=1]; (т.е., второй инкремент в середине выражения)
Т.е., префиксные операции выполняются перед вычислением выражения, а постфиксные после.
Сначала вычисляется левое выражение, а потом правое, а потом уже присвоение [с вычислением].
« Последнее редактирование: 20-11-2007 16:58 от Алексей1153++ » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
NetRaider
Гость
« Ответ #11 : 06-10-2003 10:07 » 

Цитата

проанализировал еще раз ассемблерный код


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

Цитата

Т.е., префиксные операции выполняются перед вычислением выражения, а постфиксные после.


Это неверное утверждение. На основании анализа двух строк кода нельзя восстановить логику работы компилятора в других случаях.

Например это (VC6):
int i = 0;
i = i++;

Угадай что получится ?
Записан
Asan
Гость
« Ответ #12 : 06-10-2003 10:25 » 

Цитата: NetRaider

Например это (VC6):
int i = 0;
i = i++;
Угадай что получится ?

Гм, ноль получается, действительно...
Объясни, пожалуйста, в чем тут прикол (а то что-то с бодуна мозги плохо варят  Жаль   Так больше нельзя...   Вот такой я вот   Ха-ха-ха  :twisted:  Отлично  :oops:
Записан
NetRaider
Гость
« Ответ #13 : 06-10-2003 10:31 » 

i = i++;
Эта строка не является правильно  с точки зрения Стандарта языка. Результат ее выполнения не определен. Почему, см. выше. Что получится, зависит от компилятора, опций и много чего еще. Может быть format c: Ага.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #14 : 06-10-2003 10:42 » 

но если сменится поведение нового компилятора, тогда - косяк Отлично
Записан

RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #15 : 06-10-2003 10:43 » 

Цитата
i=i++;
Странно было бы встретить такое выражение.

Насчет стандарта - gcc, вроде, соответствует ANSI.
Вот что получилось с этим примером:
Код:
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
; i=0
movl $0, -4(%ebp)
; i=i++
movl -4(%ebp), %eax
movl %eax, -4(%ebp) ; запись правого значения в левое - i=0
leal -4(%ebp), %eax
incl (%eax) ; i++
movl -4(%ebp), %eax
; return i
movl %eax, %eax
leave
ret
Если нет уверености в порядке вычисления, то есть такая хорошая вешь - круглые скобки. Конечно, можно и просто разбить выражение на несколько строк.

Вот подобное выражение я применял не раз и оно меня не подводило: a=a[i++]; тут все просто.
Несколько пост-преф. для одной переменной в одном выражении - не нашел применения.
« Последнее редактирование: 20-11-2007 16:59 от Алексей1153++ » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Asan
Гость
« Ответ #16 : 06-10-2003 10:47 » 

Цитата: NetRaider
i = i++;
Эта строка не является правильно  с точки зрения Стандарта языка. Результат ее выполнения не определен. Почему, см. выше. Что получится, зависит от компилятора, опций и много чего еще. Может быть format c: Ага.

Спасибо за ответ,   правда,  и сам уже вроде допер.  format c: - это кочно круто  Отлично
А вообще то, ИМХО,         i = i++     -  это скорее к психиатру, а не в форум  Улыбаюсь
Записан
NetRaider
Гость
« Ответ #17 : 06-10-2003 10:47 » 

Цитата

Вот что получилось с этим примером:


его на vc6 компилировать надо - там 1 получается
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #18 : 06-10-2003 10:57 » 

Этот странный пример собирал в gcc с ключами управления диалектами:
-ansi
-std=c89  (iso9899 1990)
-std=c99  (iso9899 1999)
-std=gnu89
-std=gnu99

Результат один и тот же - i==1
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Asan
Гость
« Ответ #19 : 06-10-2003 11:06 » 

VC7      -    0
Intel7    -   0
gcc       -    1
  Вот такой я вот
Записан
NetRaider
Гость
« Ответ #20 : 06-10-2003 11:16 » 

Цитата

Вот подобное выражение я применял не раз и оно меня не подводило: a=a[i++]; тут все просто.

Здесь тоже undefined behavior. Когда нибудь может рухнуть а-ля:
Цитата

VC7 - 0
Intel7 - 0
gcc - 1


Хотя, как там в пословице про жареного петуха... Ага
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #21 : 06-10-2003 11:37 » 

Попытаюсь все-таки докапаться до того, какой инкремент выполнялся когда:
Код:
int calc(char *a,char *b) {
    int i,j;

    i=1;
    j=0;
    a[i++]=b[j++];
    a[++i]=b[j++];
    a[++i]=b[++j];
    return i+j; /* без этого gcc уничтожает i и j как класс и выполняет прямое копирование */
    }

int main() {
    char a[10],b[10];
    int i;

    for(i=0;i<10;i++) a[i]=b[i]=(char)i; /* это и return calc() поставил для того чтобы запутать оптимизатор  - чтобы не думал что это ненужно */
    return calc(a,b);
    }
Результат. Привожу только calc().
Код:
calc:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
; i=1
movl $1, -4(%ebp)
; j=0
movl $0, -8(%ebp)
; a[i++]=b[j++]
movl -4(%ebp), %eax
movl 8(%ebp), %edx
addl %eax, %edx  ; edx=&a[i]
movl -8(%ebp), %eax
addl 12(%ebp), %eax  ; eax=&b[j]
movb (%eax), %al
movb %al, (%edx)  ; a[1]=b[0]
leal -8(%ebp), %eax  ; i++
incl (%eax)
leal -4(%ebp), %eax)  ; j++
incl (%eax
; a[++i]=b[j++]
leal -4(%ebp), %eax  ; i++
incl (%eax)
movl -4(%ebp), %eax
movl 8(%ebp), %edx
addl %eax, %edx
movl -8(%ebp), %eax
addl 12(%ebp), %eax
movb (%eax), %al  ; a[3]=b[1]
movb %al, (%edx)
leal -8(%ebp), %eax  ; j++
incl (%eax)
; a[++i]=b[++j]
leal -4(%ebp), %eax  ; i++
incl (%eax)
movl -4(%ebp), %eax
movl 8(%ebp), %edx
addl %eax, %edx
leal -8(%ebp), %eax  ; j++
incl (%eax)
movl -8(%ebp), %eax
addl 12(%ebp), %eax
movb (%eax), %al  ; a[4]=b[3]
movb %al, (%edx)
; return i+j
movl -8(%ebp), %eax
addl -4(%ebp), %eax
movl %eax, %eax  ; return 7
leave
ret

NetRaider, если не сложно, приведи код от VC7

Насчет петуха - в VC5 и VC6 работало
« Последнее редактирование: 20-11-2007 17:00 от Алексей1153++ » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #22 : 06-10-2003 11:48 » 

люди, давайте поменьше применять непереносимых(в смысле компиляции текста) хитростей!

а ведь было такое: когда писали ещё в основном только на ассемблере, у некоторых процессоров нескольким hex-кодам соответствовали недокументированные команды. То есть разработчики ничего про эти коды не упоминали, там на бумаге были прочерки. Но эти команды работали и выполняли какое-нибудь действие! (кому не лень определяли свойства команды методом научного втыка - как мы сейчас) Правда стабильно они работали не на всех процах даже той же серии. Про next вообще молчу.

Но тогда делалось для чего? Сэкономить пару байт/тактов!
сейчас это несерьёзно. И непроссефионально Ага


 :arrow: до этого мне на ящик пришло извещение

"порядок выполнения постфикса и префикса"
со ссылкой
http://shelek.com/forum/viewtopic.php?p=15009#15009

я переходил по ней, а мне говорили: "Ты чё ваще? Такой темы не существует"

- чё за комиссия? Может -фиксы подвели Отлично
Записан

RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #23 : 06-10-2003 12:18 » 

Алексей1153, вероятно это было когда я одну свою тему редактировал - вместо "правка" нажал "цитата" ну и ситема создала второе сообщение, а первое я замочил.

Предлогаешь вообще не использовать ++/-- в выражениях?
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #24 : 06-10-2003 14:56 » 

Цитата

Предлогаешь вообще не использовать ++/-- в выражениях?

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

фиксы я уважаю - особенно если на бумаге приходится алгоритмы писать.

а писать зю=зю+1   просто обламывает!
Записан

Vorlon
Гость
« Ответ #25 : 06-10-2003 18:47 » 

Ну-ну, приколисты Жжешь
Цитата
i=i++;

это ж надо додуматься. Отлично

Вы еще проверьте
++i=i;
- компилятор не возникает, а вот
i++=i;
не нравится.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #26 : 06-10-2003 20:12 » 

Цитата
i++=i;

Тут неопределенна граница операторов - проблема разбора строки. То ли i+ +=i , то ли i++ =i . Оберни скобками: (i++)=i, но тогда получится некоректное  левое значение. Отлично
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
NetRaider
Гость
« Ответ #27 : 07-10-2003 00:08 » 

Цитата

Попытаюсь все-таки докапаться до того, какой инкремент выполнялся когда:


Я не понимаю, почему правильность работы кода проверяется путем эксперимента и анализа ассемблерного кода.  Выражения подобные i=i++ определены Стандартом как вызывающие undefined behavior. Эксперементально можно выяснить как компилятор поступает в таких случаях, но надеяться, что все компиляторы будут поступать также, несколько опрометчиво.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #28 : 07-10-2003 02:01 » 

неопределённое поведение - это обезьяна с гранатой,
которая сказала, случайно выдернув чеку
"не расстраивайтесь, у меня ещё есть"
Записан

RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #29 : 07-10-2003 08:26 » 

NetRaider,

в контексе странного примера (i=i++) это может быть и неимеет смысла. Вот можешь ли сказать какой по стандарту (какому именно? О чем ты гоаорил? О ANSI С?) будет порядок вычисления для "a[i]=b[j]" - когда вычисляется "i", а когда "j" (рассматривай это не как переменные, а как выражения)?
« Последнее редактирование: 20-11-2007 17:01 от Алексей1153++ » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Страниц: [1] 2  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines