Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« : 02-10-2003 02:16 » |
|
: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++] аналогично.
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #2 : 02-10-2003 05:53 » |
|
то есть если i в выражении используется более одного раза, то лучше :?:
|
|
« Последнее редактирование: 20-11-2007 16:21 от Алексей1153++ »
|
Записан
|
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #3 : 02-10-2003 08:22 » |
|
Привет, Лёха, нет тут "лучше-хуже". В зависимости от того, что тебе надо используешь пост- или префикс. Ключевая фраза у NetRaider: Тип операции (постфиксная/префиксная) влияет только на то, какое значение будет использовано в качестве результата подвыражения i++ (или ++i). и точка следования между двумя модификациями в 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
|
|
« Ответ #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++ »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #6 : 02-10-2003 10:27 » |
|
то есть - засада у зоосада... int main() { return 6; }
-- а куда остальное делось? массивы?
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #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++ »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
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
|
|
« Ответ #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 » |
|
Например это (VC6): int i = 0; i = i++; Угадай что получится ?
Гм, ноль получается, действительно... Объясни, пожалуйста, в чем тут прикол (а то что-то с бодуна мозги плохо варят :twisted: :oops:
|
|
|
Записан
|
|
|
|
NetRaider
Гость
|
|
« Ответ #13 : 06-10-2003 10:31 » |
|
i = i++; Эта строка не является правильно с точки зрения Стандарта языка. Результат ее выполнения не определен. Почему, см. выше. Что получится, зависит от компилятора, опций и много чего еще. Может быть format c: .
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #14 : 06-10-2003 10:42 » |
|
но если сменится поведение нового компилятора, тогда - косяк
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #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 » |
|
i = i++; Эта строка не является правильно с точки зрения Стандарта языка. Результат ее выполнения не определен. Почему, см. выше. Что получится, зависит от компилятора, опций и много чего еще. Может быть format c: . Спасибо за ответ, правда, и сам уже вроде допер. format c: - это кочно круто А вообще то, ИМХО, i = i++ - это скорее к психиатру, а не в форум
|
|
|
Записан
|
|
|
|
NetRaider
Гость
|
|
« Ответ #17 : 06-10-2003 10:47 » |
|
Вот что получилось с этим примером:
его на vc6 компилировать надо - там 1 получается
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #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
|
|
« Ответ #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++ »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #22 : 06-10-2003 11:48 » |
|
люди, давайте поменьше применять непереносимых(в смысле компиляции текста) хитростей! а ведь было такое: когда писали ещё в основном только на ассемблере, у некоторых процессоров нескольким hex-кодам соответствовали недокументированные команды. То есть разработчики ничего про эти коды не упоминали, там на бумаге были прочерки. Но эти команды работали и выполняли какое-нибудь действие! (кому не лень определяли свойства команды методом научного втыка - как мы сейчас) Правда стабильно они работали не на всех процах даже той же серии. Про next вообще молчу. Но тогда делалось для чего? Сэкономить пару байт/тактов! сейчас это несерьёзно. И непроссефионально :arrow: до этого мне на ящик пришло извещение "порядок выполнения постфикса и префикса" со ссылкой http://shelek.com/forum/viewtopic.php?p=15009#15009я переходил по ней, а мне говорили: "Ты чё ваще? Такой темы не существует" - чё за комиссия? Может -фиксы подвели
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #23 : 06-10-2003 12:18 » |
|
Алексей1153, вероятно это было когда я одну свою тему редактировал - вместо "правка" нажал "цитата" ну и ситема создала второе сообщение, а первое я замочил.
Предлогаешь вообще не использовать ++/-- в выражениях?
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #24 : 06-10-2003 14:56 » |
|
Предлогаешь вообще не использовать ++/-- в выражениях?
ни в коем разе!!! просто делать это красиво и без двусмысленностей фиксы я уважаю - особенно если на бумаге приходится алгоритмы писать. а писать зю=зю+1 просто обламывает!
|
|
|
Записан
|
|
|
|
Vorlon
Гость
|
|
« Ответ #25 : 06-10-2003 18:47 » |
|
Ну-ну, приколисты i=i++; это ж надо додуматься. Вы еще проверьте ++i=i; - компилятор не возникает, а вот i++=i; не нравится.
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #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. Эксперементально можно выяснить как компилятор поступает в таких случаях, но надеяться, что все компиляторы будут поступать также, несколько опрометчиво.
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #28 : 07-10-2003 02:01 » |
|
неопределённое поведение - это обезьяна с гранатой, которая сказала, случайно выдернув чеку "не расстраивайтесь, у меня ещё есть"
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #29 : 07-10-2003 08:26 » |
|
NetRaider,
в контексе странного примера (i=i++) это может быть и неимеет смысла. Вот можешь ли сказать какой по стандарту (какому именно? О чем ты гоаорил? О ANSI С?) будет порядок вычисления для "a[i]=b[j]" - когда вычисляется "i", а когда "j" (рассматривай это не как переменные, а как выражения)?
|
|
« Последнее редактирование: 20-11-2007 17:01 от Алексей1153++ »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
|