J>Мне вот интересно, а что должно получится в результате следующего выражения в переменной j:
int i=1;
int j;
j=(++i)*2+ --i;
Это известный пример так называемого неопределенного поведения, что является одной из худших ошибок программирования, т.к. в отличие от других она может "прикидываться" вполне законной конструкцией. В данном случае сказываются следующие моменты:
1) в общем модификация и запись в ячейку памяти не является атомарной операцией (на некоторых системах компилятору, например, требуется загрузить значение в регистр, инкрементировать его, после чего записать его обратно);
2) стандарт не требует от компилятора генерации "постоянной" записи промежуточных значений (подобно тому как кэшируются операции с диском);
3) попытка одновременной записи в одну ячейку может закончиться очень и очень плохо: от изменяющегося от компилятора к компилятору (самый вероятный сценарий), от билда к билду (тоже бывает), или даже от запуска к запуску значения выражения до нестабильной работы системы и/или core dump в зависимости от самых различных факторов;
4) хорошей новостью является то, что в C++ определены так называемые точки следования (sequence points), в которых гарантируется, что любые операции записи таки совершились, например, точками следования сопровождаются вызовы функций, операции ||, && и т.д.;
5) в общем случае одну и ту же переменную между двумя точками следования безопасно можно модифицировать только один раз;
6) так вот, в приведенном выражении между двумя модификациями i нет точки следования, что приводит к неопределенному поведению, иначе говоря, компилятор волен творить любые фокусы, как иногда шутят, даже генерировать код для форматирования диска.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Ничего хорошего. Всё зависит от компилятора. Это как задачка с пятью плюсами.
В справочнике у Страуструпа написано про это. И зачем так делать — запутать врага?
А как же про корректно написанный код и всё — такое?
Здравствуйте Павел Кузнецов, Вы писали:
ПК>1) в общем модификация и запись в ячейку памяти не является атомарной операцией (на некоторых системах компилятору, например, требуется загрузить значение в регистр, инкрементировать его, после чего записать его обратно); ПК>2) стандарт не требует от компилятора генерации "постоянной" записи промежуточных значений (подобно тому как кэшируются операции с диском); ПК>3) попытка одновременной записи в одну ячейку может закончиться очень и очень плохо: от изменяющегося от компилятора к компилятору (самый вероятный сценарий), от билда к билду (тоже бывает), или даже от запуска к запуску значения выражения до нестабильной работы системы и/или core dump в зависимости от самых различных факторов; ПК>4) хорошей новостью является то, что в C++ определены так называемые точки следования (called sequence) points), в которых гарантируется, что любые операции записи таки совершились, например, точками следования сопровождаются вызовы функций, операции ||, && и т.д.; ПК>5) в общем случае одну и ту же переменную между двумя точками следования безопасно можно модифицировать только один раз; ПК>5) так вот, в приведенном выражении между двумя модификациями i нет точки следования, что приводит к неопределенному поведению, иначе говоря, компилятор волен творить любые фокусы, как иногда шутят, даже генерировать код для форматирования диска.
Такая интерпретация точек следования и неопределенного поведения не совсем точна, она проста для запоминания, но надо понимать, что она не полностью соответствует действительности. Начнем с простых примеров:
int i;
i = i = 1;
Здесь хотя значение переменной меняется дважды между точками следования, никакого неопределенного поведения не возникает, и результат по Стандарту должен быть один: переменная i должна иметь значение 1. Компилятор обязан сгенерировать код, который не должен приводить к фатальным последствиям даже в случае, если целевой процессор имеет два конвейера, и если они будут одновременно писать в одну и ту же ячейку памяти произойдет сбой. Так как результат этого примера не зависит от порядка вычислений.
Более сложный пример:
int i;
i = ++i;
Так же результат определен. Любая последовательность вычислений допустимых по Стандарту будет, приводит к одному и тому же результату.
Еще более ложный пример:
int i,j,k;
i = (i = j + k) + (i = j - k);
Здесь на основании статического анализа нельзя сказать будет ли неопределенное поведение при выполнении этого фрагмента кода. При j == 0, k == 0 и i == 0 результат четко определен, так как любая последовательность вычислений будет приводить к одному и тому же результату, при других значениях результат не определен и возникает неопределенное поведение.
Последний пример наглядно показывает всю сложность проблемы, если бы надо было контролировать только двукратное изменение одной переменной между точками следования, то такой анализ можно провести статически и компиляторы бы это делали и выдавали соответствующие ошибки при компиляции. Компилятор при компиляции может обнаружить только потенциальное нарушение и может выдать только предупреждение, но никак не ошибку, так как в зависимости от значений переменных поведение может быть как жестко определенным, так и неопределенным и приводить к печальным последствиям.
Здравствуйте dupamid, Вы писали:
D>>>Число последовательностей вычисления аргументов у функции с n параметрами будет n!, что для 13 параметров больше чем может принимать 32-х разрядное число.
ПК>>Это не важно. Главное, что все они потенциально могут быть перечислены.
D>Так и значения любого числа с ограниченным числом бит тоже могут быть перечислены, даже если не все из них могут получаться в результате арифметических операций. Так что большой разницы я не вижу.
Могут быть перечислены все потенциальные комбинации битов. Однако, если некоторые из этих комбинаций недопустимы, то варианты дальнейшего поведения программы в общем случае перечислены быть не могут.
D>>>Так как в компьютерах арифметика с ограниченной точностью, то все значение переменной тоже можно теоретически перечислить и переменная, скорее всего, будет иметь одно из этих значений.
ПК>>Не вполне. Например, значащие биты скалярного типа могут занимать меньше чем sizeof(тип) * CHAR_BIT. При одновременной модификации значения результат может оказаться не представимым данной битовой маской со всеми вытекающими вплоть до core dump. Например, на Cray не все биты некоторых скалярных типов являются значащими.
D>Формула sizeof(тип) * CHAR_BIT вообще в корне не верна – она не позволяет вычислить число бит в типе.
Формула sizeof(тип) * CHAR_BIT позволяет вычислить число бит в объектном представлении типа (object representation), т.е. число бит, которое необходимо для хранения значения данного типа. Возможно, что не все из этих бит будут значащими (value representation).
3.9 Types 4 The object representation of an object of type T is the sequence of N unsigned char objects taken up
by the object of type T, where N equals sizeof(T). The value representation of an object is the set of bits that hold the value of type T. For POD types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementationdefined set of values.
D>Пример, может быть система, на которой sizeof(type) для всех встроенных типов и «обычных указателей» равен 1, но число бит и диапазоны значений для типов разные.
Нет, такая система невозможна.
3.9.1 Fundamental types 1 For character types, all bits of the object representation participate in the value representation. For unsigned character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types.
Кроме того,
2 “signed char”, “short int”, “int”, and “long int.” In this list, each type provides at least as much storage as those preceding it in the list.
Таким образом, т.к. все биты представления char являются значащими, и short представляет не меньший диапазон значений, чем char, а также sizeof(short) == 1, все биты short также являются значащими. То же по индукции для остальных целочисленных типов.
D>То, что некоторые комбинации бит в типе могут быть недопустимы, и приводить к печальным последствиям не означает, что их (значения) нельзя все перечислить.
Но нельзя перечислить варианты поведения программы при образовании недопустимых битовых комбинаций. Это и означает undefined behavior.
ПК>>Прерывания, так же как и (почти?) любая асинхронность, являются понятиями за пределами стандарта. Поэтому, фактически, любое асинхронное взаимодействие само по себе автоматом приводит к undefined behavior.
D>Сигналы не являются понятиями за пределами Стандарта, а они асинхронны.
Именно поэтому я и написал "почти". Но стандарт, фактически, не определяет семантику сигналов, а просто констатирует их наличие. Однако, если в твоем примере заменить прерывание на сигнал, то:
[i]9 When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects with type other than volatile sig_atomic_t are unspecified, and the value of any object not of volatile sig_atomic_t that is modified by the handler becomes undefined.[/b]
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: Неопределенное поведение, побочные эффекты и оптимиза
Здравствуйте dupamid, Вы писали:
D>Здравствуйте Павел Кузнецов, Вы писали:
ПК>> <skipped>
D>Такая интерпретация точек следования и неопределенного поведения не совсем точна, она проста для запоминания, но надо понимать, что она не полностью соответствует действительности. Начнем с простых примеров:
D>Еще более ложный (сложный?) пример: D>
D>int i,j,k;
D>i = (i = j + k) + (i = j - k);
D>
D>Здесь на основании статического анализа нельзя сказать будет ли неопределенное поведение при выполнении этого фрагмента кода. При j == 0, k == 0 и i == 0 результат четко определен, так как любая последовательность вычислений будет приводить к одному и тому же результату, при других значениях результат не определен и возникает неопределенное поведение.
Поскольку компьютер (в т.ч. компилятор и порожденная программа) — это как-бы детерминированное устройство, то поведение всегда "определено".
Так что говорить о том "если бы все были = 0..." — немного не то.
imho, неопределенное поведение — это такое явление, когда на основании только исходного описания (программа на Си + конвенции) принципиально невозможно вывести результат.
D>Последний пример наглядно показывает всю сложность проблемы, если бы надо было контролировать только двукратное изменение одной переменной между точками следования, то такой анализ можно провести статически и компиляторы бы это делали и выдавали соответствующие ошибки при компиляции. Компилятор при компиляции может обнаружить только потенциальное нарушение и может выдать только предупреждение, но никак не ошибку, так как в зависимости от значений переменных поведение может быть как жестко определенным, так и неопределенным и приводить к печальным последствиям.
Еще одни грабли (не называющиеся, впрочем, неопределенным поведением) — это вызов функций с побочным эффектом в бинарных операторах.
int print(int i)
{
printf("%d\n", i);
return i;
}
void main()
{
int x = 3 + print(1) + 4 + print(2) + 5; // специально напихал побольше слагаемых - чтобы оптимизация была
}
Компилятору нет указаний о порядке вычисления, поэтому в разных условиях будет цирк.
Более навороченный случай:
int x;
int sideffect(int y, int z)
{
x = y;
return z;
}
void test(int a, int b, int c)
{
x = a;
int t = x * sideffect(b, c);
}
// варианты трансляции:
{
int t1 = x; // = aif(t1 == 0)
t = 0; // x = 0, t = 0else
t = t1 * sideffect(b,c); // x = b, t = a*c
}
{
int t1 = x;
t = t1 * sideffect(b,c); // x = b; t = a*c
}
{
int t1 = sideffect(b,c); // x = b; t1 = c
t = x * t1; // x = b; t = b*c
}
Еще одно аналогичное явление — использование не-volatile переменных (оптимизирующий компилятор может кэшировать не-volatile переменные, и в результате побочных эффектов — например, многозадачности, — произойдет рассогласование кэша и оригинала).
Здравствуйте Павел Кузнецов, Вы писали:
ПК>До TC уже недолго осталось: Andrew Koenig где-то говорил, что уже подали в ISO, стало быть где-то в районе нескольких месяцев – года до публикации. В любом случае, следующая версия стандарта ожидается где-то к 2008 или около того — не так уж и долго по вселенским меркам
ТС = тот свет?
В общем, можно поздравить: С++ достаточно высокоорганизован, чтобы попасть под теорему Гёделя.
Оказывается, в нём есть выражения, про которые даже нельзя сказать, undefined они или нет!
И кто бы подумал, что одно из них — банальное x=y=0
Слава богу, что компиляторы на таких философских вопросах не застревают, а тихо делают своё чёрное дело.
Пойду пить безалкогольное , а то совсем башню оторвёт. Никому нельзя верить.
Здравствуйте Кодт, Вы писали:
ПК>>До TC уже недолго осталось: Andrew Koenig где-то говорил, что уже подали в ISO, стало быть где-то в районе нескольких месяцев – года до публикации. <...>
К>ТС = тот свет? :???:
Technical Corrigenda, содержит исправления ошибок, найденных в стандарте, может выпускаться чаще, чем новые версии последнего.
К>В общем, можно поздравить: С++ достаточно высокоорганизован, чтобы попасть под теорему Гёделя.
Под теоремы Геделя подпадают любые логические системы, использующие арифметику.
К>Оказывается, в нём есть выражения, про которые даже нельзя сказать, undefined они или нет! :beer:
То же относится и к любому другому языку. Undefined behavior во многих случаях определяется в период выполнения.
К>И кто бы подумал, что одно из них — банальное x=y=0 :wow:
Только в том случае, если x и y являются именами одного и того же объекта.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте dupamid, Вы писали:
ПК>>Действительно, для подобных простых примеров, вероятность получить что-нибудь уж очень неожиданное на скалярных платформах не велика. Отчасти это происходит из-за того, что компиляторы "понимают" подобные простые случаи.
D>Она не просто «не велика», с практической точки зрения можно сказать, что она равна нулю, так как во многих случаях такая поддержка есть на аппаратном уровне и компиляторам вообще ничего специального делать не надо.
Не равна. Например, в comp.lang.c[++][.moderated] сообщалось, что один из реальных компиляторов для `i = 2; ... i = ++i;' порождал код, в результате которого i == 4 (этот вопрос даже вынесен в comp.lang.c FAQ). Причем это легко объяснить, если рассмотреть `i = i++', например, в таком контексте:
int i = 2;
...
x = i + 1; // после этой точки `i + 1' уже может находиться в регистре
...
i = ++i; // здесь компилятор вполне может этим воспользоваться
Последовательность инструкций, в данном случае схематично могла быть такой:
mov i, 2 ; i = 2 i == 2
mov r, 2 ; поместить значение i в регистр r == 2
...
inc r ; вычислить i + 1 r == 3
mov x, r ; x = i + 1
...
mov i, r ; i = i + 1 i == 3
inc i ; ++i i == 4
Как видишь, она вполне логична, т.к. нет лишних чтений значения i из памяти (т.к. i не является volatile, подобная оптимизация со стороны компилятора вполне допустима).
ПК>>Compaq C Compiler умеет выдавать сообщения вида:
D>Это, насколько я понимаю, компилятор С,
Без разницы.
Вообще:
1) Компиляторы C часто служат back-end'ом для компиляторов C++ или имеют общий back-end.
2) Все, что реализуемо в С относительно модификации скалярных объектов между точками следования, реализуемо и в C++.
В частности:
1) Есть сильное подозрение, что Compaq C++ Compiler делает то же самое.
2) Как уже было сказано, gcc 3.0 делает то же самое.
D>С++ обладает сложной, часто скрытой семантикой
Ладно, заканчиваем с "философией". Пример, пожалуйста, в котором нужна предлагаемая "фича". Не "вообще", а в частности, т.е. фрагмент кода.
D>и говорю, что для С++ можно и нужно сделать поведение в этом случае не специфицированным
(Даже, если бы это было возможно) Зачем? Для поощрения написания ошибочного кода? Все равно такой код не становится более переносимым, а программа — более корректной или безопасной. Описание вариантов поведения в общем случае заняло бы кучу ресурсов и еще больше усложнило бы стандарт и жизнь разработчиков компиляторов без каких-либо практических выгод. Как ты предлагаешь описать варианты поведения программы? Значения объектов — все возможные комбинации битов в объектном представлении? Это никак не тянет на unspecified (см. также пример ниже).
D>С язык более низкого уровня и может быть менее безопасным.
1) Какие более низкоуровневые возможности есть в С, которых нет в C++?
2) Один из основных принципов проектирования C++: "Не оставлять места ни для каких языков более низкого уровня, кроме ассемблера".
3) Большинство компиляторов C++ одновременно являются и компиляторами C, при этом back-end у них общий. Очень сомневаюсь, что комитет пойдет на порождение подобных дорогостоящих для реализации различий.
4) Одними из главных задач ближайшей редакции стандарта является улучшение поддержки низкоуровневого/системного программирования в C++ и совместимости с C.
D>>>Теперь другой пример: ПК>><... ++*p + ++*q ...> ПК>>Ну, насчет других вариантов — легко ПК>>i == 2 j == 3[/code]
D>Через пять минут, после того как я послал пример, понял, что возможен вариант «j == 3», но это ничего не меняет, все равно количество вариантов для этого примера сильно ограничено.
Если ты думаешь, что возможные варианты сводятся к i == [1, 2], j == [2, 3, 4], то я с легкостью нарисую тебе логичную последовательность инструкций, приводящую к другим результатам. Если ты предполагаешь другой диапазон — скажи, и я снова-таки предоставлю тебе логичную последовательность инструкций, не только приводящую к другому результату, но и влияющую на дальнейшее поведение программы, вплоть до нарушения семантики основных инструкций типа if. Учти, что следует рассматривать вычисления подобных выражений в контексте, где компилятору, например, может быть выгодно использовать результаты предыдущих вычислений и т.п.:
Вполне возможна последовательность, в результате которой `(*p != *q) == true' если *p и *q указывают на один и тот же объект — типичный пример undefined behavior. В противном случае компилятор вынужден генерировать код, неоднократно загружающий значения *p и *q, хотя они и не являются volatile. Некоторые так и делают, но накладывать подобные ограничения на все компиляторы...
Описывать варианты в общем случае не представляется практически ценным, да и возможным. Собственно, это и есть главный аргумент, почему поведение undefined, а не unspecified. Существование SIMD (Single Instruction Multiple Data) архитектур, позволяющих параллельно выполнять операции с CREW (Concurrent Read Exclusive Write) памятью только усугубляет ситуацию.
ПК>>Кроме того, если отойти слегка в сторону, например, сообщалось, что SCO Optimizing C compiler на одной из платформ для: ПК>>
ПК>>int a = 123, b = 7654;
ПК>>a ^= b ^= a ^= b;
ПК>>генерирует код, устанавливающий b в 123 и a в 0.
D>и опять дело только в значении.
Но значение уже не из множества предсказуемых вариантов, что совсем не тянет на unspecified.
ПК>>В принципе, в свое время в comp.lang.c++.moderated сообщалось, что есть устройства, имеющие (кросс-)компилятор C, для которых запись в одну и ту же ячейку не может осуществляться без задержки. В противном случае, происходит что-нибудь "страшное".
D>Вот я хотел бы увидеть такую систему
Мне тоже интересно, но лично не встречал. Насколько я понимаю, всякие контроллеры и т.п. вспомогательные железяки. Однако, мы с тобой не специалисты по embended системам и микроконтроллерам, а при написании стандарта таковые присутствовали. В любом случае, это не столь существенно, т.к. описывать варианты поведения даже без этих проблем не представляется нужным, а то и возможным.
D>>>Если говориться, что существуют процессоры где есть С++ и накладные расходы на неспецифицированное поведение настолько велики, что Стандарт не может их потребовать, то прошу приводить конкретные примеры иначе спор становиться бессмысленным.
Несмотря на то, что подобные архитектуры не являются главным аргументом, почему поведение undefined, они существуют. Еще раз: векторизирующие процессоры типа Cray и т.п., ILP процессоры типа Intel Itanium и т.п., всякие другие параллельные динозавры-суперкомпьютеры. Вся суть существования этих процессоров заключается как раз в том, чтобы инструкции выполнялись параллельно, а кроме того, "Intel Itanium architecture", например, "prohibits scheduling a ld.a and ld.c in the same cycle if both instructions have the same target register", что означает фактический запрет для компилятора использовать эту инструкцию в случае выражений с указателями в случае, если пытаться тем или иным образом уточнять поведение в рассматриваемом случае.
D>Вот я и хочу увидеть хоть одну систему, где действительно происходит undefined behavior, я слышу только слова о теоретический возможности. Реальные примеры есть?
Сколько угодно: undefined behavior сейчас происходит на всех системах. Undefined behavior это не "что-то страшное", а поведение, не определенное стандартом. Посмотри на другие источники undefined behavior:
— разыменование нулевого указателя (на подавляющем большинстве систем само по себе не приводит ни к каким проблемам до использования полученной ссылки);
— файл, не заканчивающийся символом новой строки (единственный из известных мне компиляторов, который хоть как-то на это прореагировал это Intel C++ Compiler);
— появление символов \ в именах заголовочных файлов (на DOS/Windows платформах этим даже пользуются стандартные API-библиотеки);
— целочисленная константа, не помещающаяся в long int;
— нестандартные escape-sequences;
— использование invalid pointer-value;
— использование типа из union не того, который был в нем сохранен (эта "фича" активно используется);
— использование неинициализированных данных (это уж и вовсе близко к обсуждаемой теме);
— переполнение знакового целого типа (что тоже вполне близко);
— операция взятие адреса объекта неполного типа, полный тип которого переопределяет operator &()... и т.д.
Имхо, множественная модификация скалярных объектов между соседними точками следования вполне неплохо вписывается в общую схему undefined behavior: те места, где стандарт по тем или иным причинам не вообще не ограничивает разработчиков компилятора или где предполагается возможность, что данные будут находиться в непредсказуемом состоянии.
Unspecified же относится к более ясным вещам, где есть определенный выбор вариантов:
— порядок вычисления подвыражений и аргументов (не ясен только порядок, данные запорчены быть не могут, и сами варианты предсказуемы);
— вызывается ли деструктор для type_info (какая кому разница?);
— вызывается ли функция распределения памяти до или после вычисления аргументов конструктора;
— некоторые сравнения указателей (результат неизвестен, но всегда true или false);
— нужно ли место для хранения ссылки (нам все равно, т.к. язык не позволяет узнать об этом)... и т.д.
Никто пока не посчитал нужным/не смог придумать простую схему описания вариантов в рассматривающемся случае. Если ты придумаешь такую, то появятся шансы (исчезающе маленькие) на изменение на unspecified, если, конечно, разработчики оптимизирующих компиляторов и приверженцы параллельных вычислений не будут слишком протестовать, а они будут, уж поверь
D>Что значит не опробованные фичи? Значения по умолчанию для параметров шаблонных функций (Item 226 Default template arguments for function templates), это как опробованная фича или нет, или, например, (Default arguments in template template-parameters 184).
Хотя это и достаточно тривиальное исправление непоследовательности, как и, скажем, template typedef, в наших же интересах, чтобы это не вошло в стандарт до экспериментальной проверки.
ПК>>Это не выбор: или — или. Должно быть: и — и. D>Я не утверждаю, что такой выбор это хорошо, но реальная жизнь такова, что часто вопрос стоит именно так «или – или».
Вернемся на землю. Например, какая "реальная жизнь" применительно к процессу стандартизации? На мой взгляд, реальнее, чем existing practice уж и придумать что-нибудь тяжело.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте Павел Кузнецов, Вы писали:
ПК>Это, интересно, откуда такие сведения? Все известные мне высказывания членов комитета стандартизации говорят как раз об обратном.
Пока я пишу подробный ответ по остальным вопросам, посмотри ссылочку http://www.research.att.com/~bs/kbh-C++0x.pdf, это он говорил перед комитетом по стандартизации в мае прошлого года в Копенгагене. Хорошо, что я нашел это в открытом виде, а то этот документ у меня был из источника, откуда сведения я не могу разглашать, наше через www.altavista.com.
Здравствуйте Павел Кузнецов, Вы писали:
ПК>3) попытка одновременной записи в одну ячейку может закончиться очень и очень плохо: от изменяющегося от компилятора к компилятору (самый вероятный сценарий), от билда к билду (тоже бывает), или даже от запуска к запуску значения выражения до нестабильной работы системы и/или core dump в зависимости от самых различных факторов;
ПК>5) так вот, в приведенном выражении между двумя модификациями i нет точки следования, что приводит к неопределенному поведению, иначе говоря, компилятор волен творить любые фокусы, как иногда шутят, даже генерировать код для форматирования диска.
А кстати, как может возникнуть критический код?
Разве что компилятор вставит jmp на код, который по его недо-разумению никода не должен выполняться.
Вот хотелось бы узнать...
Перекуём баги на фичи!
Re[4]: Неопределенное поведение, побочные эффекты и оптимиза
Здравствуйте Кодт, Вы писали:
К>Поскольку компьютер (в т.ч. компилятор и порожденная программа) — это как-бы детерминированное устройство, то поведение всегда "определено". К>Так что говорить о том "если бы все были = 0..." — немного не то.
В Стандарте выполнение программы описывается ввиде абстрактной машины для которой нет требования детерминированности 1.9/1, так что говорить об это можно и нужно.
1.9
1. The semantic descriptions in this International Standard define a parameterized nondeterministic abstract machine. This International Standard places no requirement on the structure of conforming implementations. In particular, they need not copy or emulate the structure of the abstract machine. Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.5)
5) This provision is sometimes called the “asif” rule, because an implementation is free to disregard any requirement of this International Standard as long as the result is as if the requirement had been obeyed, as far as can be determined from the observable behavior of the program. For instance, an actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no side effects affecting the observable behavior of the program are produced.
3. Certain other aspects and operations of the abstract machine are described in this International Standard as unspecified (for example, order of evaluation of arguments to a function). Where possible, this International Standard defines a set of allowable behaviors. These define the nondeterministic aspects of the abstract machine. An instance of the abstract machine can thus have more than one possible execution sequence for a given program and a given input.
4. Certain other operations are described in this International Standard as undefined (for example, the effect of dereferencing the null pointer). [Note: this International Standard imposes no requirements on the behavior of programs that contain undefined behavior.]
Здравствуйте dupamid, Вы писали:
ПК>> <...> в общем случае одну и ту же переменную между двумя точками следования безопасно можно модифицировать только один раз; <...> в приведенном выражении между двумя модификациями i нет точки следования, что приводит к неопределенному поведению
D>Такая интерпретация точек следования и неопределенного поведения не совсем точна, она проста для запоминания, но надо понимать, что она не полностью соответствует действительности.
Напротив, именно это и является руководством к идентификации неопределенности поведения в подобных случаях:
5 Expressions 4 Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.
D>Начнем с простых примеров: D>
D>int i;
D>i = i = 1;
D>
D>Здесь хотя значение переменной меняется дважды между точками следования, никакого неопределенного поведения не возникает, и результат по Стандарту должен быть один: переменная i должна иметь значение 1.
Это где стандарт определяет такое поведение?
D> <...> Так как результат этого примера не зависит от порядка вычислений.
Это никак не влияет на определенность поведения. Неопределенность возникает именно из-за того, что содержимое ячейки меняется дважды между точками следования, не важно меняется ли оно на одно и то же значение или нет.
D>Более сложный пример: D>
D>int i;
D>i = ++i;
D>
D>Так же результат определен.
Снова-таки, пример неопределенного поведения. Можешь поискать свою подстроку в видах "i = ++i", "i = i++", "x = ++x" и "x = x++" или "assignment increment undefined behavior" в comp.std.c++ или comp.lang.c++.moderated — там это регулярно обсуждается.
См. также C faq
D>Любая последовательность вычислений допустимых по Стандарту будет, приводит к одному и тому же результату.
Никакая последовательность действий не определяется стандартом. Стандарт, согласно 5/4, умывает руки и поведение всей программы является неопределенным.
D>Еще более ложный пример: D>
D>int i,j,k;
D>i = (i = j + k) + (i = j - k);
D>
D>Здесь на основании статического анализа нельзя сказать будет ли неопределенное поведение при выполнении этого фрагмента кода.
Можешь не гадать: оно уже есть.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[4]: Неопределенное поведение, побочные эффекты и оптимиза
Здравствуйте Кодт, Вы писали:
К>Еще одни грабли (не называющиеся, впрочем, неопределенным поведением) — это вызов функций с побочным эффектом в бинарных операторах.
Это называется unspecified behavior. Т.е., в данном случае, порядок побочных эффектов неопределен. Программа в целом при этом остается в границах, описываемых стандартом. При undefined behavior же, стандарт не накладывает никаких ограничений на поведение программы, т.е. это, можно сказать, больше не программа на C++.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: Чудеса инкремента...
От:
Аноним
Дата:
04.09.02 13:26
Оценка:
Здравствуйте dupamid, Вы писали:
D>Такая интерпретация точек следования и неопределенного поведения не совсем точна, она проста для запоминания, но надо понимать, что она не полностью соответствует действительности.
Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.
D>int i;
D>i = i = 1;
Undefined behavior.
D>int i;
D>i = ++i;
Undefined behavior.
D>Еще более ложный пример:
Sic!
D>int i,j,k;
D>i = (i = j + k) + (i = j - k);
Undefined behavior.
(Ну и, строго говоря, в двух последних примерах неопределенное поведение еще раньше — из-за использования неинициализированной переменной.)
Право, Вы бы уж что-нибудь более сложное придумали. Вот хоть такое:
int i = 0;
i = (i++, 0) + (i++, 0);
Или в очередной раз вспомнили бы про опечатку undefined vs. unspecified.
Re[4]: Неопределенное поведение, побочные эффекты и оптимиза
От:
Аноним
Дата:
04.09.02 13:42
Оценка:
Здравствуйте Кодт, Вы писали:
К>imho, неопределенное поведение — это такое явление, когда на основании только исходного описания (программа на Си + конвенции) принципиально невозможно вывести результат.
Примерно так.
behavior, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements.
К>Еще одни грабли (не называющиеся, впрочем, неопределенным поведением) — это вызов функций с побочным эффектом в бинарных операторах.
К>int print(int i)
К>{
К> printf("%d\n", i);
К> return i;
К>}
К>void main()
К>{
К> int x = 3 + print(1) + 4 + print(2) + 5; // специально напихал побольше слагаемых - чтобы оптимизация была
К>}
К>Компилятору нет указаний о порядке вычисления, поэтому в разных условиях будет цирк.
Будет только разная последовательность вывода чисел 1 и 2. Это — unspecified behavior, а не undefined.
К>Более навороченный случай:
К>int x;
К>int sideffect(int y, int z)
К>{
К> x = y;
К> return z;
К>}
К>void test(int a, int b, int c)
К>{
К> x = a;
К> int t = x * sideffect(b, c);
К>}
К>// варианты трансляции:
К>{
К> int t1 = x; // = a
К> if(t1 == 0)
К> t = 0; // x = 0, t = 0
К> else
К> t = t1 * sideffect(b,c); // x = b, t = a*c
К>}
К>{
К> int t1 = x;
К> t = t1 * sideffect(b,c); // x = b; t = a*c
К>}
К>{
К> int t1 = sideffect(b,c); // x = b; t1 = c
К> t = x * t1; // x = b; t = b*c
К>}
Из этих вариантов трансляции возможен только второй.
К>Еще одно аналогичное явление — использование не-volatile переменных (оптимизирующий компилятор может кэшировать не-volatile переменные, и в результате побочных эффектов — например, многозадачности, — произойдет рассогласование кэша и оригинала).
Здравствуйте Павел Кузнецов, Вы писали:
ПК>Напротив, именно это и является руководством к идентификации неопределенности поведения в подобных случаях:
ПК>5 Expressions ПК>4 Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.
Крайне интересная ссылочка! Приведу ее полностью:
5
4. Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.53) Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined. [Example:
i = v[i++]; // the behavior is unspecified
i = 7, i++, i++; // i becomes 9
i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented
—end example]
Как тут уже заметили undefined или unspecified! Это две большие разницы. Если все таки unspecified, то результаты выполнения этих примеров жестко определены.
D>>Начнем с простых примеров: D>>
D>>int i;
D>>i = i = 1;
D>>
D>>Здесь хотя значение переменной меняется дважды между точками следования, никакого неопределенного поведения не возникает, и результат по Стандарту должен быть один: переменная i должна иметь значение 1.
ПК>Это где стандарт определяет такое поведение?
А где он это запрещает.
D>> <...> Так как результат этого примера не зависит от порядка вычислений.
ПК>Это никак не влияет на определенность поведения. Неопределенность возникает именно из-за того, что содержимое ячейки меняется дважды между точками следования, не важно меняется ли оно на одно и то же значение или нет.
D>>Более сложный пример: D>>
D>>int i;
D>>i = ++i;
D>>
D>>Так же результат определен.
ПК>Снова-таки, пример неопределенного поведения. Можешь поискать свою подстроку в видах "i = ++i", "i = i++", "x = ++x" и "x = x++" или "assignment increment undefined behavior" в comp.std.c++ или comp.lang.c++.moderated — там это регулярно обсуждается.
ПК>См. также C faq
Это ссылка про С 95 года да и про пост-инкримент, а не про прединкримент.
D>>Любая последовательность вычислений допустимых по Стандарту будет, приводит к одному и тому же результату.
ПК>Никакая последовательность действий не определяется стандартом. Стандарт, согласно 5/4, умывает руки и поведение всей программы является неопределенным.
D>>Еще более ложный пример: D>>
D>>int i,j,k;
D>>i = (i = j + k) + (i = j - k);
D>>
D>>Здесь на основании статического анализа нельзя сказать будет ли неопределенное поведение при выполнении этого фрагмента кода.
ПК>Можешь не гадать: оно уже есть.
Re[5]: Неопределенное поведение, побочные эффекты и оптимиза
Здравствуйте Аноним, Вы писали:
К>>Более навороченный случай:
К>>int x;
К>>int sideffect(int y, int z)
К>>{
К>> x = y;
К>> return z;
К>>}
К>>void test(int a, int b, int c)
К>>{
К>> x = a;
К>> int t = x * sideffect(b, c);
К>>}
К>>// варианты трансляции:
К>>{
К>> int t1 = x; // = a
К>> if(t1 == 0)
К>> t = 0; // x = 0, t = 0
К>> else
К>> t = t1 * sideffect(b,c); // x = b, t = a*c
К>>}
К>>{
К>> int t1 = x;
К>> t = t1 * sideffect(b,c); // x = b; t = a*c
К>>}
К>>{
К>> int t1 = sideffect(b,c); // x = b; t1 = c
К>> t = x * t1; // x = b; t = b*c
К>>}
А>Из этих вариантов трансляции возможен только второй.
???
"Дорожные знаки написаны кровью".
Этот пример родился как память о нескольких днях муторной отладки, когда дебаг-версия жужжала, а релиз — нет. Только там выражение позаковыристей было (на десяток строчек), а то, что сделала оптимизация — ууууу!
Здравствуйте dupamid, Вы писали:
D>Крайне интересная ссылочка! Приведу ее полностью: D>5 D>4. Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.53)
^ При обсуждении данного вопроса это можно выкинуть.
D>Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression.
^ В принципе, этого было бы достаточно, но для полноты картины следующее тоже не помешает.
D>Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.
Примеры не являются нормативной частью стандарта, кроме того, уже известно (хотя и не закреплено голосованием комитета), что в данном содержатся две опечатки, касающиеся именно слов unspecified, так что остается именно то, что я и процитировал изначально.
D>[Example: D>i = v[i++]; // the behavior is unspecified D>i = 7, i++, i++; // i becomes 9 D>i = ++i + 1; // the behavior is unspecified D>i = i + 1; // the value of i is incremented D>—end example]
D>Как тут уже заметили undefined или unspecified! Это две большие разницы. Если все таки unspecified, то результаты выполнения этих примеров жестко определены.
Все-таки undefined.
D>А где он это запрещает.
В процитированном кусочке (выделено мною): "Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. ... The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined" В стандарте слово shall означает обязательность выполнения требования. Программа, не соответствующая данному требованию, выходит за рамки стандарта, т.е. ее поведение is undefined, что для ясности в сложных случаях еще и подкреплено последним предложением.
D>>>Более сложный пример: D>>>
D>>>int i;
D>>>i = ++i;
D>>>
D>>>Так же результат определен.
<...>
ПК>>См. также C faq
D>Это ссылка про С 95 года да и про пост-инкримент, а не про прединкримент.
Это не важно, эти правила были унаследованы из C в неизменном виде. Более того, там где явно не сказано обратное, C++ соответствует стандарту C89/90, так как включает его по ссылке. Кроме того, мог бы все-таки поискать, как тебе предлагалось в архивах comp.std.c++ — это официальная группа для разрешения подобных вопросов.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[5]: Чудеса инкремента...
От:
Аноним
Дата:
04.09.02 14:36
Оценка:
Здравствуйте dupamid, Вы писали:
D>Крайне интересная ссылочка! Приведу ее полностью: D>5 D>4. Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.53) Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined. [Example: D>i = v[i++]; // the behavior is unspecified D>i = 7, i++, i++; // i becomes 9 D>i = ++i + 1; // the behavior is unspecified D>i = i + 1; // the value of i is incremented D>—end example]
D>Как тут уже заметили undefined или unspecified! Это две большие разницы. Если все таки unspecified, то результаты выполнения этих примеров жестко определены.
Здравствуйте Аноним, Вы писали:
D>>Как тут уже заметили undefined или unspecified! Это две большие разницы. Если все таки unspecified, то результаты выполнения этих примеров жестко определены.
А>DR уже подал Andrew Koenig: http://anubis.dkuug.dk/JTC1/SC22/WG21/docs/cwg_active.html#351
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[6]: Неопределенное поведение, побочные эффекты и оптимиза
От:
Аноним
Дата:
04.09.02 14:54
Оценка:
Здравствуйте Кодт, Вы писали:
К>>>int x;
К>>>int sideffect(int y, int z)
К>>>{
К>>> x = y;
К>>> return z;
К>>>}
К>>>void test(int a, int b, int c)
К>>>{
К>>> x = a;
К>>> int t = x * sideffect(b, c);
К>>>}
К>>>// варианты трансляции:
К>>>{
К>>> int t1 = x; // = a
К>>> if(t1 == 0)
К>>> t = 0; // x = 0, t = 0
К>>> else
К>>> t = t1 * sideffect(b,c); // x = b, t = a*c
К>>>}
А>>Из этих вариантов трансляции возможен только второй. К>???
К>"Дорожные знаки написаны кровью". К>Этот пример родился как память о нескольких днях муторной отладки, когда дебаг-версия жужжала, а релиз — нет. Только там выражение позаковыристей было (на десяток строчек), а то, что сделала оптимизация — ууууу!
Брррр... То есть Вы хотите сказать, что мог быть выброшен вызов функции с побочными эффектами, если один из операндов умножения — ноль?.. М-да, что ж это за компилятор такой?
До TC уже недолго осталось: Andrew Koenig где-то говорил, что уже подали в ISO, стало быть где-то в районе нескольких месяцев – года до публикации. В любом случае, следующая версия стандарта ожидается где-то к 2008 или около того — не так уж и долго по вселенским меркам :-)
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[7]: Неопределенное поведение, побочные эффекты и оптимиза
Здравствуйте Аноним, Вы писали:
А>Здравствуйте Кодт, Вы писали:
А>Брррр... То есть Вы хотите сказать, что мог быть выброшен вызов функции с побочными эффектами, если один из операндов умножения — ноль?.. М-да, что ж это за компилятор такой?
VC5 (а может еще и 4 — уже не помню).
Перекуём баги на фичи!
Re[10]: Чудеса инкремента...
От:
Аноним
Дата:
04.09.02 16:15
Оценка:
Здравствуйте Кодт, Вы писали:
К>В общем, можно поздравить: С++ достаточно высокоорганизован, чтобы попасть под теорему Гёделя. К>Оказывается, в нём есть выражения, про которые даже нельзя сказать, undefined они или нет! К>И кто бы подумал, что одно из них — банальное x=y=0
C++ очень сложный язык — даже авторы стандарта его не знают. Но только это и позволило ему стать языком номер один.
К>Пойду пить безалкогольное , а то совсем башню оторвёт. Никому нельзя верить.
Небольшое лирическое отступление. Меня здесь все бросились критиковать и пинать ногами это хорошо! Значит мы доберемся до истины (если это сейчас возможно, хотя видимо пока невозможно), а то форум стал превращаться в погоню за баллами, когда мнения людей из топа не оспариваются, а с ними молча соглашаются и ставят баллы. Мне это мало интересно, мне интересно узнавать что-то новое. Чем больше изучаешь С++ тем больше понимаешь, что всего знать нельзя и не ошибаться тоже нельзя, С++ лишком большой и ты всегда чего-то не знаешь или думаешь, что знаешь, но ошибаешься. Поэтому я доволен, когда я ошибаюсь и меня поправляют – значит я узнаю что-то новое или восполняю пробелы, чтобы следующий раз не ошибаться. Могу сказать, что даже Страуструп не всегда бывает прав, правде если он бывает не прав, то обычно исправляют стандарт
Теперь по делу. То, о чем мы спорим однозначного решения пока не имеет, об этом еще дискутируют в комитете по стандартизации С++ http://www.dkuug.dk/jtc1/sc22/wg21. Достаточно посмотреть две темы 222 Sequence points and lvalue-returning operators и 351 Sequence point error: unspecified or undefined?. Первая в состоянии drafting, а вторая вообще в состоянии open. В 222 теме примеры аналогичные моим – типа i = i = 0, предлагаемое решение добавление точки следования в операторе присваивания. Про 351 нет даже предварительного мнения, когда оно будет тогда и будем говорить undefined или unspecified, пока я вижу двухкратное упоминание unspecified, и однократное undefined, так что с математической точки зрения вероятнее, что опечатка undefined.
Здравствуйте dupamid, Вы писали:
D>Теперь по делу. То, о чем мы спорим однозначного решения пока не имеет,
Прежде всего давай определимся, о чем идет речь. Я утверждаю, что согласно текущей редакции стандарта модификация скалярных объектов более одного раза между соседними точками следования приводит к неопределенному поведению программы. Если ты споришь с этим, то продолжаем, иначе, пожалуйста, сформулируй свою позицию.
D>об этом еще дискутируют в комитете по стандартизации С++ http://www.dkuug.dk/jtc1/sc22/wg21. Достаточно посмотреть две темы 222 Sequence points and lvalue-returning operators и 351 Sequence point error: unspecified or undefined?. Первая в состоянии drafting, а вторая вообще в состоянии open.
В 222 подтверждается, что в текущей редакции стандарта — основной нормативный документ для разрешения подобных разногласий — конструкции вида (a += b) += c; приводят к неопределенному поведению. С другой стороны, утверждается, что подобные конструкции могут быть полезны и для этого предлагается изменить существующую спецификацию, причем не в той части, которая вызвала разногласия. Кроме того, уточняются некоторые формулировки относительно случаев типа x = y = 0. Но это уже все совсем другие "пироги".
D>предлагаемое решение добавление точки следования в операторе присваивания.
Это не разрешение (несуществующей) неоднозначности, это изменение существующей спецификации, к предмету разговора никакого отношения не имеющее. Когда все операции присваивания, в т.ч. и "простые", будут сопровождаться точками следования, тогда некоторые из приводимых тобой примеров станут примерами не установленного (unspecified behavior), а некоторые и вовсе приобретут точно определенное поведение. Однако это никоим образом не влияет на основной тезис, т.е. не добавит определенности в случае неоднократного изменения скалярных объектов между соседними точками следования, и уж вовсе никак не влияет на настоящее положение дел в целом.
D>Про 351 нет даже предварительного мнения, когда оно будет тогда и будем говорить undefined или unspecified, пока я вижу двухкратное упоминание unspecified, и однократное undefined, так что с математической точки зрения вероятнее, что опечатка undefined.
Нормативные документы типа стандарта C++ не являются чем-то, что следует трактовать согласно подобным соображениям. Напоминаю, что примеры не являются нормативной частью стандарта и при любых противоречиях примеров и основного текста предпочтение должно отдаваться последнему, независимо от количества примеров. Кроме того, как уже упоминалось, в спорных случаях трактовки стандарта, следует обращаться в comp.std.c++ — официальную группу комитета стандартизации. В последнее время там было несколько обсуждений, касавшихся (не)определенности поведения конструкций вида i = ++i. Ни в одном из известных мне подобных обсуждений между членами комитета не было разногласий относительно трактовки процитированного ранее пункта — все единодушно подтверждали наличие неопределенного поведения при модификации скалярных объектов более одного раза между точками следования. Если вдруг комитет разрешит 351 в пользу unspecified — что, imho, фактически невероятно, иначе, зачем весь сыр-бор с 222? — мы будем считать эти случаи таковыми. До тех же пор все эти случаи, согласно стандарту, приводят к undefined behavior.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте Павел Кузнецов, Вы писали:
ПК>Прежде всего давай определимся, о чем идет речь. Я утверждаю, что согласно текущей редакции стандарта модификация скалярных объектов более одного раза между соседними точками следования приводит к неопределенному поведению программы. Если ты споришь с этим, то продолжаем, иначе, пожалуйста, сформулируй свою позицию.
Я утверждаю, что ясности даже с учетом только текущего Стандарта нет. Так как нет даже точной формулировки, что примеры в Стандарте только информативны. Если поискать слово “normative” в Стандарте C++, то первое интересующее нас вхождение будет в 17.3.1.1\2:
Paragraphs labelled ‘‘Note(s):’’ or ‘‘Example(s):’’ are informative, other paragraphs are normative.
Так что в описании библиотеки примеры действительно только информативны, но про core language такое я сказать не могу. В Стандарте С99 ясно сказано Foreword\6:
Annexes D and F form a normative part of this standard; annexes A, B, C, E, G, H, I, J, the bibliography, and the index are for information only. In accordance with Part 3 of the ISO/IEC Directives, this foreword, the introduction, notes, footnotes, and examples are also for information only.
Если бы все примеры в стандарте были информативны, то 17.3.1.1\2 должен был находиться в начале стандарта (как в C99), а не в середине, где речь идет только о стандартной библиотеке, это ясно и 17.3.1.1\1.
D>>об этом еще дискутируют в комитете по стандартизации С++ http://www.dkuug.dk/jtc1/sc22/wg21. Достаточно посмотреть две темы 222 Sequence points and lvalue-returning operators и 351 Sequence point error: unspecified or undefined?. Первая в состоянии drafting, а вторая вообще в состоянии open.
ПК>В 222 подтверждается, что в текущей редакции стандарта — основной нормативный документ для разрешения подобных разногласий — конструкции вида (a += b) += c; приводят к неопределенному поведению. С другой стороны, утверждается, что подобные конструкции могут быть полезны и для этого предлагается изменить существующую спецификацию, причем не в той части, которая вызвала разногласия. Кроме того, уточняются некоторые формулировки относительно случаев типа x = y = 0. Но это уже все совсем другие "пироги".
Я бы сказал не изменить, а точно определить! Так как в текущей версии Стандарта на такие вопросы четких ответов нет.
D>>предлагаемое решение добавление точки следования в операторе присваивания.
ПК>Это не разрешение (несуществующей) неоднозначности, это изменение существующей спецификации, к предмету разговора никакого отношения не имеющее. Когда все операции присваивания, в т.ч. и "простые", будут сопровождаться точками следования, тогда некоторые из приводимых тобой примеров станут примерами не установленного (unspecified behavior), а некоторые и вовсе приобретут точно определенное поведение. Однако это никоим образом не влияет на основной тезис, т.е. не добавит определенности в случае неоднократного изменения скалярных объектов между соседними точками следования, и уж вовсе никак не влияет на настоящее положение дел в целом.
Все зависит от того, где именно будет точка следования, до выполнения присваивания или после. Пока речь идет о добавлении точки следования после выполнения оператора присваивания и до получения результата операции присваивания. Но окончательное решение мы не узнаем как минимум до мая 2003 когда, так как нужно еще как минимум два-три митинга так как тема должна пройти еще два состояния Review и Ready и только потом будет окончательное решение.
D>>Про 351 нет даже предварительного мнения, когда оно будет тогда и будем говорить undefined или unspecified, пока я вижу двухкратное упоминание unspecified, и однократное undefined, так что с математической точки зрения вероятнее, что опечатка undefined.
ПК>Нормативные документы типа стандарта C++ не являются чем-то, что следует трактовать согласно подобным соображениям. Напоминаю, что примеры не являются нормативной частью стандарта и при любых противоречиях примеров и основного текста предпочтение должно отдаваться последнему, независимо от количества примеров. Кроме того, как уже упоминалось, в спорных случаях трактовки стандарта, следует обращаться в comp.std.c++ — официальную группу комитета стандартизации. В последнее время там было несколько обсуждений, касавшихся (не)определенности поведения конструкций вида i = ++i. Ни в одном из известных мне подобных обсуждений между членами комитета не было разногласий относительно трактовки процитированного ранее пункта — все единодушно подтверждали наличие неопределенного поведения при модификации скалярных объектов более одного раза между точками следования. Если вдруг комитет разрешит 351 в пользу unspecified — что, imho, фактически невероятно, иначе, зачем весь сыр-бор с 222? — мы будем считать эти случаи таковыми. До тех же пор все эти случаи, согласно стандарту, приводят к undefined behavior.
i = i = 1; — здесь я думаю, вопросов не возникнет.
i = (i = j + k) + (i = j — k); — здесь поведение будет зависеть от значений.
i = ++i; — здесь вопросы есть
Я считаю, что правильное решение по 351 это сделать поведение не специфицированным, так как иначе при работе с указателями неопределенное поведением может возникать слишком часто в безобидных ситуациях, или сделать точку следования перед выполнением оператора присваивания.
P.S. отвечу на мелкий упрек про неинициализированные данные в моих примерах: считайте, что все переменные объявлены глобальными, а вообще объявления я приводил только для того чтобы тип переменных был точно определен.
Здравствуйте dupamid, Вы писали:
D>Я утверждаю, что ясности даже с учетом только текущего Стандарта нет. Так как нет даже точной формулировки, что примеры в Стандарте только информативны. Если поискать слово “normative” в Стандарте C++, то первое интересующее нас вхождение будет в 17.3.1.1\2: <...>
Стандарт ISO/IEC 14882:1998(E) не является "вещью в себе". Учитывая теорему Геделя ;-), надо искать не в стандарте, накладывающем ограничения на язык C++, а в стандартах, накладывающих ограничения на стандарты ISO/IEC :-) На момент подачи заявки на ISO/IEC 14882:1998(E), если не ошибаюсь, действительными являлись ISO/IEC Directives, Part 3, 1997: Rules for the structure and drafting of International Standards (может, 1989 edition, но в интересующих нас пунктах они не отличаются, а у меня под рукой этого артефакта нет). Итак (1997 Directives, Part 3, внутритекстовые выделения мои):
3.4 normative elements
those elements setting out the provisions to which it is necessary to conform in order to be able to claim compliance with the standard
3.5 informative elements 3.5.1 preliminary elements
those elements that identify the standard, introduce its content and explain its background, its development and its relationship with other standards 3.5.2 supplementary elements
those elements that provide additional information intended to assist the understanding or use of the standard
6.5 Other informative elements 6.5.1 Notes and examples integrated in the text
Notes and examples integrated in the text of a standard shall only be used for giving additional information intended to assist the understanding or use of the standard and shall not contain provisions to which it is necessary to conform in order to be able to claim compliance with the standard.
Для полноты сразу уж и насчет сносок:
6.5.2 Footnotes to the text
Footnotes to the text give additional information; their use shall be kept to a minimum. They shall not contain requirements.
Слово shall, выделенное мною в 6.5.1, согласно ISO/IEC Guide 2:1996, definition 7.1, означает, что данное предложение является требованием, обязательным для выполнения. Таким образом, стандарт ISO/IEC просто не может включать нормативные требования во внутритекстовые примеры и примечания.
D>Если бы все примеры в стандарте были информативны, то 17.3.1.1\2 должен был находиться в начале стандарта (как в C99), а не в середине, где речь идет только о стандартной библиотеке, это ясно и 17.3.1.1\1.
В самом стандарте ISO/IEC 14882:1998(E) дополнительно эти, а также многие другие требования и определения, касающиеся самого стандарта, повторять не обязательно (хотя и можно), они общие для всех стандартов ISO/IEC.
ПК>>В 222 подтверждается, что в текущей редакции стандарта <...> приводят к неопределенному поведению. С другой стороны, утверждается, что подобные конструкции могут быть полезны и для этого предлагается изменить существующую спецификацию, причем не в той части, которая вызвала разногласия. <...>
D>Я бы сказал не изменить, а точно определить! Так как в текущей версии Стандарта на такие вопросы четких ответов нет.
Есть, есть, продолжай медитировать на фразу "Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression." ;-)
D>>>предлагаемое решение добавление точки следования в операторе присваивания.
ПК>>Когда все операции присваивания, в т.ч. и "простые", будут сопровождаться точками следования, тогда некоторые из приводимых тобой примеров станут примерами не установленного (unspecified behavior), а некоторые и вовсе приобретут точно определенное поведение. Однако это никоим образом не влияет на основной тезис, т.е. не добавит определенности в случае неоднократного изменения скалярных объектов между соседними точками следования, и уж вовсе никак не влияет на настоящее положение дел в целом.
D>Все зависит от того, где именно будет точка следования, до выполнения присваивания или после.
Для предмета спора не важно. Главное, что неоднократная модификация скалярных объектов между соседними точками следования приводит к неопределенному поведению. Предлагаемые изменения никак этого не меняют, а только добавляют новые точки следования в некоторых местах, где в настоящий момент их определенно нет.
ПК>>До тех же пор все эти случаи, согласно стандарту, приводят к undefined behavior.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Вы опять прислали очень интересные ссылки, спасибо! Давайте присмотримся к ним повнимательнее.
ПК>3.4 normative elements ПК>those elements setting out the provisions to which it is necessary to conform in order to be able to claim compliance with the standard
ПК>6.5 Other informative elements ПК>6.5.1 Notes and examples integrated in the text ПК>Notes and examples integrated in the text of a standard shall only be used for giving additional information intended to assist the understanding or use of the standard and shall not contain provisions to which it is necessary to conform in order to be able to claim compliance with the standard.
В последнем абзаце только утверждается, что реализация может не поддерживать приведенные примеры и при этом оставаться конформной Стандарту, так как должна следовать общему тексту, а не конкретным примерам. Это очень понятно, так как примеры могут быть не полны и, следовательно, будут не компилироваться в том виде, как они приведены в стандарте и т.д. Но приведенная выше формулировка не говорит, что суть примеров приводимых в Стандарте не имеет значения (и просто является ошибочной) и в них можно писать все что угодно.
ПК>Для полноты сразу уж и насчет сносок:
ПК>6.5.2 Footnotes to the text ПК>Footnotes to the text give additional information; their use shall be kept to a minimum. They shall not contain requirements.
Это вообще рекомендация писателям Стандарта, что они должны стремится к тому, чтобы все требования находились в основном тексте Стандарта, а не где-то еще.
ПК>Слово shall, выделенное мною в 6.5.1, согласно ISO/IEC Guide 2:1996, definition 7.1, означает, что данное предложение является требованием, обязательным для выполнения. Таким образом, стандарт ISO/IEC просто не может включать нормативные требования во внутритекстовые примеры и примечания.
Тут все понятно, значение слова shall в Стандарте тоже проблем не вызывает.
D>>Если бы все примеры в стандарте были информативны, то 17.3.1.1\2 должен был находиться в начале стандарта (как в C99), а не в середине, где речь идет только о стандартной библиотеке, это ясно и 17.3.1.1\1.
ПК>В самом стандарте ISO/IEC 14882:1998(E) дополнительно эти, а также многие другие требования и определения, касающиеся самого стандарта, повторять не обязательно (хотя и можно), они общие для всех стандартов ISO/IEC.
Я согласен, что их можно не повторять, но если они все-таки повторяются, то с какой-то целью, а тем более, если они повторяются не в начале Стандарта, а в его середине и касающейся конкретной его части. Кстати приложения тоже являются информативными, однако в Стандарте С++ явно сказано, что часть приложений нормативна.
ПК>>>В 222 подтверждается, что в текущей редакции стандарта <...> приводят к неопределенному поведению. С другой стороны, утверждается, что подобные конструкции могут быть полезны и для этого предлагается изменить существующую спецификацию, причем не в той части, которая вызвала разногласия. <...>
D>>Я бы сказал не изменить, а точно определить! Так как в текущей версии Стандарта на такие вопросы четких ответов нет.
ПК>Есть, есть, продолжай медитировать на фразу "Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression." ;-)
Продолжаю, только интересующая нас часть находиться дальше: "The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined." и пример далее. А тут уже сложнее, и одной медитацией не обойдешься, надо подождать 351.
D>>>>предлагаемое решение добавление точки следования в операторе присваивания.
ПК>>>Когда все операции присваивания, в т.ч. и "простые", будут сопровождаться точками следования, тогда некоторые из приводимых тобой примеров станут примерами не установленного (unspecified behavior), а некоторые и вовсе приобретут точно определенное поведение. Однако это никоим образом не влияет на основной тезис, т.е. не добавит определенности в случае неоднократного изменения скалярных объектов между соседними точками следования, и уж вовсе никак не влияет на настоящее положение дел в целом.
D>>Все зависит от того, где именно будет точка следования, до выполнения присваивания или после.
ПК>Для предмета спора не важно. Главное, что неоднократная модификация скалярных объектов между соседними точками следования приводит к неопределенному поведению. Предлагаемые изменения никак этого не меняют, а только добавляют новые точки следования в некоторых местах, где в настоящий момент их определенно нет.
Пока не ясно к чему она приводит, есть два мнения либо к не специфицированному, либо к не определенному, надо ждать 351.
ПК>>>До тех же пор все эти случаи, согласно стандарту, приводят к undefined behavior.
Здравствуйте dupamid, Вы писали:
ПК>>Notes and examples integrated in the text of a standard shall only be used for giving additional information intended to assist the understanding or use of the standard and shall not contain provisions to which it is necessary to conform in order to be able to claim compliance with the standard.
D>В последнем абзаце только утверждается, что реализация может не поддерживать приведенные примеры и при этом оставаться конформной Стандарту, так как должна следовать общему тексту, а не конкретным примерам.
1) Нормативные положения (provision) стандарта определяют требования, рекомендации и т.п. к реализации.
2) В последнем абзаце утверждается, что ни один стандарт ISO/IEC не может содержать нормативные положения (provisions) во внутритекстовых примерах и примечаниях.
3) В применении к предмету разговора это означает, что если в нормативной части сказано, что модификация скалярного объекта между двумя соседними точками следования приводит к неопределенному поведению, а в примере подобная ситуация помечена комментарием `unspecified', то поведение undefined, т.к. никакая реализация не обязана следовать комментариям в примере.
D>приведенная выше формулировка не говорит, что суть примеров приводимых в Стандарте не имеет значения
Приведенная формулировка говорит, что значение примеров заключается только в предоставлении дополнительной информации, предназначенной для облегчения понимания основной (нормативной) части. Если в примере содержится ошибка, это означает, что пример со своей задачей не справился.
D>(и просто является ошибочной) и в них можно писать все что угодно.
Этого я и не утверждал, поосторожнее с модификацией позиции собеседника.
ПК>>Footnotes to the text give additional information; their use shall be kept to a minimum. They shall not contain requirements.
D>Это вообще рекомендация писателям Стандарта, что они должны стремится к тому, чтобы все требования находились в основном тексте Стандарта, а не где-то еще.
Это не рекомендации, а требования (shall), которые заключаются в том, что
1) Использование сносок должно быть минимизировано.
2) Сноски не могут содержать требования. В данном случае requirement это термин. Нормативные положения (provision) стандартов делятся на: statement (3.8.1), instruction (3.8.2), recommendation (3.8.3) и, наконец, requirement (3.8.4). Таким образом, в отличие от внутритекстовых примеров и примечаний, сноски могут содержать нормативные положения, кроме требований.
ПК>>Таким образом, стандарт ISO/IEC просто не может включать нормативные требования во внутритекстовые примеры и примечания.
D>Тут все понятно, значение слова shall в Стандарте тоже проблем не вызывает.
Если это понятно, то должно быть также понятно, что аргументация цитатой из внутритекстового примера при наличии нормативного положения является некорректной.
D>Я согласен, что их можно не повторять, но если они все-таки повторяются, то с какой-то целью, а тем более, если они повторяются не в начале Стандарта, а в его середине и касающейся конкретной его части.
Считай это стилистическим различием. "Основную" и "библиотечную" части стандарта готовили разные люди.
D>Кстати приложения тоже являются информативными, однако в Стандарте С++ явно сказано, что часть приложений нормативна.
Есть существенная разница. Приложения, в отличие от внутритекстовых примеров и примечаний могут быть информативными или нормативными, причем, согласно п. 6.3.8 и 6.4.1 в стандарте должно быть четко указано, какие из них являются какими.
D>>>в текущей версии Стандарта на такие вопросы четких ответов нет.
D>интересующая нас часть находиться дальше: "The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined."
Если рассмотреть только нормативную часть абзаца 5/4 без затуманивающего примера, то относительно множественной модификации скалярных объектов между соседними точками следования все кристально ясно:
<... сначала про не установленный порядок вычисления ...> Между предыдущей и следующей точками следования значение скалярного объекта должен модифицироваться не более одного раза при вычислении выражения <... еще большие ограничения ...> Требования данного абзаца должны выполняться для всех возможных порядков вычисления подвыражений полного выражения, иначе поведение не определено.
Таким образом, если хотя бы для одного возможного порядка вычисления подвыражений допускается неоднократное изменение значения скалярного объекта между соседними точками следования, поведение не определено. Что здесь не ясно?!
D>Пока не ясно к чему она приводит, есть два мнения либо к не специфицированному, либо к не определенному,
Первое мнение пока что ничем, кроме ненормативного примера не подтверждается. Второе подкрепляется нормативной частью стандарта и высказываниями людей, которые составляли стандарт. Более того, под unspecified одновременная модификация скалярных объектов никак не подходит, т.к. во многих случаях невозможно перечислить возможный диапазон значений.
D>надо ждать 351.
Вот же упрямец... :-) Ну, жди. Если бы не ленился, поискал бы архивы аналогичных обсуждений в comp.std.c++ и comp.lang.c++.moderated, где принимали участие те же члены комитета, которые будут голосовать по 351. Ладно, помогу. Вот соответствующие реплики пары давних членов комитета, один из которых, помимо прочего, является разработчиком компилятора:
Francis Glassborow: >Now: the effect of ++i + ++i is *unspecified*. This means, that there is some limited number of possibilities (3?) and exactly one of them can happen. The undefined concerns the access and modification rules wich have to be met with different orderings (can they be violated here?).
Clause 5 para 4, sentence 2
Between the previous and next sequence point a scalar object SHALL (my emphasis) have its stored value modified at most once by the evaluation of an expression.
That 'shall' is mandatory and breaching it results in behaviour outside the scope of the Standard, i.e. undefined behaviour.
The example is WRONG. Both uses of unspecified should be undefined.
Steve Clamage:
multiple modifications between sequence points
The infamous cases "i = i++" and "a[i] = i++" can be detected by the compiler. Perhaps some compilers do detect them. But consider this entire compilation unit:
extern int i;
int foo(int* p) { return ++i + ++*p; }
The code is OK unless p points to i, in which case the results are not defined. If we require detection of multiple modification, the compiler would have to generate runtime code to check for aliasing (as this case is called, since different things refer to the same object), which could be very expensive. User code would also have to be prepared to deal with whatever the error reporting mechanism is -- probably an exception.
So why "undefined" instead of defining a complete ordering on expressions? It allows compilers the freedom to generate efficient code. Given a complicated expression with no sequence points, the compiler is allowed to assume that the order of side effects doesn't matter, and so can do things in the way that uses the least space and time. In cases where the order of side effects does matter, you tell the compiler by inserting a sequence point -- like a semicolon or a comma. So instead of "a[i]=i++", you can write "(a[i]=i, ++i)" if that is what you meant. In other words, compilers can generate the most efficient code possible, and only those expressions where the order of side effects matters need experience a slowdown.
Finally, why "undefined" instead of "unspecified" or "implementation defined"? The latter two cases would require defining all the possible outcomes, which isn't practical to do in the general case. (It might not even be possible.)
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте Павел Кузнецов, Вы писали:
D>>приведенная выше формулировка не говорит, что суть примеров приводимых в Стандарте не имеет значения
ПК>Приведенная формулировка говорит, что значение примеров заключается только в предоставлении дополнительной информации, предназначенной для облегчения понимания основной (нормативной) части. Если в примере содержится ошибка, это означает, что пример со своей задачей не справился.
D>>(и просто является ошибочной) и в них можно писать все что угодно.
ПК>Этого я и не утверждал, поосторожнее с модификацией позиции собеседника.
Странно, что Вы это приняли за свою, но модифицированную позицию – это мои собственные мысли.
Что касается остального, то спор себя исчерпал – решение по 351 действительно, скорее всего будет в пользу undefined, хотя это слишком сурово для С++. Для С это может сгодиться так как там нет ссылок и шаблонов, которые затуманивают текст и делают незаметным двукратную модификацию одной переменой между точками следования. Более того, на подавляющем большинстве систем ничего страшного от одновременной модификации одной и той же переменной не произойдет и формулировка unspecified более подходит, так как переменная просто получит одно из значений. Выигрыш от разрешения реализации таких оптимизаций, при которой она может не следить за одновременной модификацией переменной, крайне сомнителен. Стандарт мог бы потребовать генерацию кода, гарантирующую работу программы без сбоев в таких случаях, это не вызвало бы больших накладных расходов.
Здравствуйте dupamid, Вы писали:
ПК>>Этого я и не утверждал, поосторожнее с модификацией позиции собеседника. D>Странно, что Вы это приняли за свою, но модифицированную позицию – это мои собственные мысли.
Извини, был не в духе, чуть было не начал сорить фразами из серии "Ошибка аргументации #..."
D>Что касается остального, то спор себя исчерпал – решение по 351 действительно, скорее всего будет в пользу undefined, хотя это слишком сурово для С++. <...> на подавляющем большинстве систем ничего страшного от одновременной модификации одной и той же переменной не произойдет и формулировка unspecified более подходит, так как переменная просто получит одно из значений.
Это в случае, если модификация является атомарной и/или вычисления выполняются последовательно. А если, скажем в "++i + ++i + ++i" каждая операция ++ выполняется процессором в два-три шага, и обе операции при этом могут выполняться действительно одновременно? "Страшного" действительно, скорее всего ничего не будет, но и определить варианты принимаемых значений в общем случае тоже не представляется возможным.
D>Выигрыш от разрешения реализации таких оптимизаций, при которой она может не следить за одновременной модификацией переменной, крайне сомнителен. Стандарт мог бы потребовать генерацию кода, гарантирующую работу программы без сбоев в таких случаях, это не вызвало бы больших накладных расходов.
В применении к приведенному примеру это может означать запрет использования возможности процессора выполнять вычисления параллельно во всех выражениях вида "++*p + ++*q".
... << J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте Павел Кузнецов, Вы писали:
ПК>>>Этого я и не утверждал, поосторожнее с модификацией позиции собеседника. D>>Странно, что Вы это приняли за свою, но модифицированную позицию – это мои собственные мысли. ПК>Извини, был не в духе, чуть было не начал сорить фразами из серии "Ошибка аргументации #..."
Бывает
ПК>Это в случае, если модификация является атомарной и/или вычисления выполняются последовательно. А если, скажем в "++i + ++i + ++i" каждая операция ++ выполняется процессором в два-три шага, и обе операции при этом могут выполняться действительно одновременно? "Страшного" действительно, скорее всего ничего не будет, но и определить варианты принимаемых значений в общем случае тоже не представляется возможным.
Если проблема только в значении, то обычно это unspecified, что и было бы хорошо в этом случае.
D>>Выигрыш от разрешения реализации таких оптимизаций, при которой она может не следить за одновременной модификацией переменной, крайне сомнителен. Стандарт мог бы потребовать генерацию кода, гарантирующую работу программы без сбоев в таких случаях, это не вызвало бы больших накладных расходов. ПК>В применении к приведенному примеру это может означать запрет использования возможности процессора выполнять вычисления параллельно во всех выражениях вида "++*p + ++*q".
Да, но, специальная поддержка потребуется далеко не на всех системах, а вот неопределенное поведение возникает на всех системах. С++ имеет очень сложную семантику (часто скрытую) и ее не всегда хорошо видно по исходному тексту, так что сделать его немного более безопасным только хорошо, если при этом накладные расходы либо вообще отсутствуют, либо не очень большие.
Вы видимо удалили это свое сообщение, но я на него успел ответить
ПК>Unspecified обычно это когда варианты могут быть определены, но какой из них будет выбран неизвестно.
Во многом схожая ситуация с порядком вычисления аргументов при вызове функции, какой будет порядок зависит от слишком многих факторов и в общем случае не известен, так как даже может меняться от debug к release версии сборки. Поведение в этом случае не определено.
ПК>>>это может означать запрет использования возможности процессора выполнять вычисления параллельно во всех выражениях вида "++*p + ++*q".
D>>Да, но, специальная поддержка потребуется далеко не на всех системах,
ПК>Например, в случае "++*p + ++*q" — на всех мне известных. Например, нельзя сгенерировать последовательность такую:
ПК>
ПК>нарастить *p
ПК>нарастить *q
ПК>сложить *p и *q
ПК>
Может быть я не совсем понимаю, что Вы имеете в виду, это не то?
inc [p]
inc [q]
mov eax, [p]
add eax, [q]
D>>а вот неопределенное поведение возникает на всех системах. С++ имеет очень сложную семантику (часто скрытую) и ее не всегда хорошо видно по исходному тексту, так что сделать его немного более безопасным только хорошо, если при этом накладные расходы либо вообще отсутствуют, либо не очень большие.
ПК>Ключевое слово "если". Кроме того, если на любой из систем поведение не определено, в стандарте все равно будет undefined behavior.
Это странное утверждение. Всегда существет (или может существовать) система где самые банальные вещи могут приводить к не определенному поведению, что же тогда должны страдать все остальные системы, на которых таких проблем нет?
Здравствуйте dupamid, Вы писали:
ПК>>если, скажем в "++i + ++i + ++i" каждая операция ++ выполняется процессором в два-три шага, и обе операции при этом могут выполняться действительно одновременно? "Страшного" действительно, скорее всего ничего не будет, но и определить варианты принимаемых значений в общем случае тоже не представляется возможным.
D>Если проблема только в значении, то обычно это unspecified, что и было бы хорошо в этом случае.
Unspecified обычно это когда варианты могут быть определены, но какой из них будет выбран неизвестно.
D>специальная поддержка потребуется далеко не на всех системах, а вот неопределенное поведение возникает на всех системах. С++ имеет очень сложную семантику (часто скрытую) и ее не всегда хорошо видно по исходному тексту, так что сделать его немного более безопасным только хорошо, если при этом накладные расходы либо вообще отсутствуют, либо не очень большие.
Ключевое слово "если". Кроме того, если на любой из систем поведение не определено, в стандарте все равно будет undefined behavior.
... << J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте dupamid, Вы писали:
D>Вы видимо удалили это свое сообщение, но я на него успел ответить
Да, утром я не вполне понял, что я хотел сказать этим высказыванием:
ПК>>Например, в случае "++*p + ++*q" — на всех мне известных. Например, нельзя сгенерировать последовательность такую: <...>
Поэтому удалил эти строки и снова послал это же сообщение.
ПК>>Unspecified обычно это когда варианты могут быть определены, но какой из них будет выбран неизвестно.
D>Во многом схожая ситуация с порядком вычисления аргументов при вызове функции, какой будет порядок зависит от слишком многих факторов и в общем случае не известен, так как даже может меняться от debug к release версии сборки. Поведение в этом случае не определено.
Нет, поведение в этом случае не установлено, даже точнее не установлен порядок вычисления аргументов, т.к. в принципе можно перечислить все возможные варианты, но нельзя сказать который из них будет выбран.
D>Может быть я не совсем понимаю, что Вы имеете в виду, это не то?
Как я уже сказал, я и сам не понял, что я имел в виду
ПК>>Ключевое слово "если". Кроме того, если на любой из систем поведение не определено, в стандарте все равно будет undefined behavior.
D>Это странное утверждение. Всегда существет (или может существовать) система где самые банальные вещи могут приводить к не определенному поведению,
Например?
D>что же тогда должны страдать все остальные системы, на которых таких проблем нет?
Нет, любая реализация вольна "определить" любое неопределенное поведение.
... << J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте Павел Кузнецов, Вы писали:
ПК>>>Unspecified обычно это когда варианты могут быть определены, но какой из них будет выбран неизвестно.
D>>Во многом схожая ситуация с порядком вычисления аргументов при вызове функции, какой будет порядок зависит от слишком многих факторов и в общем случае не известен, так как даже может меняться от debug к release версии сборки. Поведение в этом случае не определено.
ПК>Нет, поведение в этом случае не установлено, даже точнее не установлен порядок вычисления аргументов, т.к. в принципе можно перечислить все возможные варианты, но нельзя сказать который из них будет выбран.
Поведение в этом случае unspecified, термин «не установлено» мне кажется не совсем удачным. Число последовательностей вычисления аргументов у функции с n параметрами будет n!, что для 13 параметров больше чем может принимать 32-х разрядное число. Так как в компьютерах арифметика с ограниченной точностью, то все значение переменной тоже можно теоретически перечислить и переменная, скорее всего, будет иметь одно из этих значений.
ПК>>>Ключевое слово "если". Кроме того, если на любой из систем поведение не определено, в стандарте все равно будет undefined behavior.
D>>Это странное утверждение. Всегда существует (или может существовать) система где самые банальные вещи могут приводить к не определенному поведению,
ПК>Например?
Гипотетический пример. Существует система, на которой значение ячейки памяти может быть записано не полностью, а частично при возникновении сигнала (прерывания), если не взвести специальный запрет на прерывание при записи. Когда сигнал будет обработан правильное значение ячейка все равно не получит. Выходит, что Стандарт должен сказать, что запись в любую ячейку памяти имеет не определенное поведение.
D>>что же тогда должны страдать все остальные системы, на которых таких проблем нет?
ПК>Нет, любая реализация вольна "определить" любое неопределенное поведение.
Может, но программа от этого все равно останется ill-formed, так как полагается на неопределенное поведение.
Здравствуйте dupamid, Вы писали:
ПК>>Нет, поведение в этом случае не установлено, даже точнее не установлен порядок вычисления аргументов, т.к. в принципе можно перечислить все возможные варианты, но нельзя сказать который из них будет выбран.
D>Поведение в этом случае unspecified, термин «не установлено» мне кажется не совсем удачным.
Whatever, но unspecified переводится именно так.
D>Число последовательностей вычисления аргументов у функции с n параметрами будет n!, что для 13 параметров больше чем может принимать 32-х разрядное число.
Это не важно. Главное, что все они потенциально могут быть перечислены.
D>Так как в компьютерах арифметика с ограниченной точностью, то все значение переменной тоже можно теоретически перечислить и переменная, скорее всего, будет иметь одно из этих значений.
Не вполне. Например, значащие биты скалярного типа могут занимать меньше чем sizeof(тип) * CHAR_BIT. При одновременной модификации значения результат может оказаться не представимым данной битовой маской со всеми вытекающими вплоть до core dump. Например, на Cray не все биты некоторых скалярных типов являются значащими.
D>>>Это странное утверждение. Всегда существует (или может существовать) система где самые банальные вещи могут приводить к не определенному поведению,
ПК>>Например?
D>Гипотетический пример. Существует система, на которой значение ячейки памяти может быть записано не полностью, а частично при возникновении сигнала (прерывания), если не взвести специальный запрет на прерывание при записи. Когда сигнал будет обработан правильное значение ячейка все равно не получит. Выходит, что Стандарт должен сказать, что запись в любую ячейку памяти имеет не определенное поведение.
Прерывания, так же как и (почти?) любая асинхронность, являются понятиями за пределами стандарта. Поэтому, фактически, любое асинхронное взаимодействие само по себе автоматом приводит к undefined behavior.
D>>>что же тогда должны страдать все остальные системы, на которых таких проблем нет? ПК>>Нет, любая реализация вольна "определить" любое неопределенное поведение. D>Может, но программа от этого все равно останется ill-formed, так как полагается на неопределенное поведение.
Нет ill-formed означает, что в программе есть синтаксические ошибки и/или диагностируемые семантические и/или нарушено One Definition Rule. Выполнение well-formed программы может приводить к undefined behavior.
... << J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте Павел Кузнецов, Вы писали:
D>>Поведение в этом случае unspecified, термин «не установлено» мне кажется не совсем удачным.
ПК>Whatever, но unspecified переводится именно так.
На мой взгляд, англоязычный термин «не специфицировано» более точно отражает смысл, но это дело вкуса и значения не имеет.
D>>Число последовательностей вычисления аргументов у функции с n параметрами будет n!, что для 13 параметров больше чем может принимать 32-х разрядное число.
ПК>Это не важно. Главное, что все они потенциально могут быть перечислены.
Так и значения любого числа с ограниченным числом бит тоже могут быть перечислены, даже если не все из них могут получаться в результате арифметических операций. Так что большой разницы я не вижу.
D>>Так как в компьютерах арифметика с ограниченной точностью, то все значение переменной тоже можно теоретически перечислить и переменная, скорее всего, будет иметь одно из этих значений.
ПК>Не вполне. Например, значащие биты скалярного типа могут занимать меньше чем sizeof(тип) * CHAR_BIT. При одновременной модификации значения результат может оказаться не представимым данной битовой маской со всеми вытекающими вплоть до core dump. Например, на Cray не все биты некоторых скалярных типов являются значащими.
Формула sizeof(тип) * CHAR_BIT вообще в корне не верна – она не позволяет вычислить число бит в типе. Пример, может быть система, на которой sizeof(type) для всех встроенных типов и «обычных указателей» равен 1, но число бит и диапазоны значений для типов разные.
То, что некоторые комбинации бит в типе могут быть недопустимы, и приводить к печальным последствиям не означает, что их (значения) нельзя все перечислить.
ПК>Прерывания, так же как и (почти?) любая асинхронность, являются понятиями за пределами стандарта. Поэтому, фактически, любое асинхронное взаимодействие само по себе автоматом приводит к undefined behavior.
Сигналы не являются понятиями за пределами Стандарта, а они асинхронны.
Здравствуйте Павел Кузнецов, Вы писали:
ПК>>>Нет, любая реализация вольна "определить" любое неопределенное поведение. D>>Может, но программа от этого все равно останется ill-formed, так как полагается на неопределенное поведение.
ПК>Нет ill-formed означает, что в программе есть синтаксические ошибки и/или диагностируемые семантические и/или нарушено One Definition Rule. Выполнение well-formed программы может приводить к undefined behavior.
Вот определения undefined и unspecified из Стандарта (выдрано из FD так как нет Стандарта под рукой, но думаю, что отличий нет). Так что программа unspecified является well-formed, а вот undefined – это ошибочная программа, для которой Стандарт ничего не гарантирует. Так что даже если реализация все уточнит программа все равно останется невалидной с точки зрения Стандарта.
1.3.12 — undefined behavior [defns.undefined]
behavior, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements. Undefined behavior may also be expected when this International Standard omits the description of any explicit definition of behavior. [Note: permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed. ]
1.3.13 — unspecified behavior [defns.unspecified]
behavior, for a well-formed program construct and correct data, that depends on the implementation. The implementation is not required to document which behavior occurs. [Note: usually, the range of possible behaviors is delineated by this International Standard. ]
Здравствуйте dupamid, Вы писали:
ПК>>>>Нет, любая реализация вольна "определить" любое неопределенное поведение. D>>>Может, но программа от этого все равно останется ill-formed, так как полагается на неопределенное поведение.
ПК>>ill-formed означает, что в программе есть синтаксические ошибки и/или диагностируемые семантические и/или нарушено One Definition Rule. Выполнение well-formed программы может приводить к undefined behavior.
D>Вот определения undefined и unspecified из Стандарта
И где из них следует, что программа, выполнение которой приводит к undefined behavior является ill-formed?
D>(выдрано из FD так как нет Стандарта под рукой, но думаю, что отличий нет). Так что программа unspecified является well-formed, а вот undefined – это ошибочная программа, для которой Стандарт ничего не гарантирует.
То, что стандарт не накладывает никаких ограничений на поведение программы, не означает, что она ill-formed. Ill-formed в общем означает, что компилятор должен выдавать диагностику (за исключением ODR, что оговорено отдельно).
D>Так что даже если реализация все уточнит программа все равно останется невалидной с точки зрения Стандарта.
Ее выполнение в общем случае, согласно стандарту, будет приводить к undefined behavior, но ill-formed она от этого не станет. Вот определение ill-formed и well-formed, медитируй
1.3.4 ill-formed
program input to a C++ implementation that is not a well-formed program (1.3.14).
1.3.14 well-formed program
a C++ program constructed according to the syntax rules, diagnosable semantic rules, and the One Definition Rule (3.2).
... << J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте dupamid, Вы писали:
ПК>>Например, в случае "++*p + ++*q" — на всех мне известных. Например, нельзя сгенерировать последовательность такую: <...>
Я вспомнил, что имелось в виду, краткость — сестра неясности Пример должен быть таким:
// (1)
поместить *p в регистр A
поместить *q в регистр B
// (2)
нарастить A
нарастить B
сложить A и B
// (3)
сохранить значение из регистра A в *p
сохранить значение из регистра B в *q
Учитывая, что если к моменту вычисления выражения значения *p и *q уже использовались и p и q не volatile, они уже могут быть во вполне подходящих регистрах. Это означает, что действия (1) и (3) в этом случае вообще могут быть опущены и сгенерирована еще более простая последовательность:
нарастить A
нарастить B
сложить A и B
Если же потребовать, чтобы результат вычисления выражения "++*p + ++*q" был определен даже в случае, если p и q указывают на один и тот же скалярный объект, то подобная оптимизация (встречающаяся на каждом шагу), станет невозможной.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте Павел Кузнецов, Вы писали:
D>>Формула sizeof(тип) * CHAR_BIT вообще в корне не верна – она не позволяет вычислить число бит в типе.
ПК>Формула sizeof(тип) * CHAR_BIT позволяет вычислить число бит в объектном представлении типа (object representation), т.е. число бит, которое необходимо для хранения значения данного типа. Возможно, что не все из этих бит будут значащими (value representation).
ПК>3.9 Types ПК>4 The object representation of an object of type T is the sequence of N unsigned char objects taken up ПК>by the object of type T, where N equals sizeof(T). The value representation of an object is the set of bits that hold the value of type T. For POD types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementationdefined set of values.
Спасибо, Вы опять покинули мне очень интересные ссылки!
D>>Пример, может быть система, на которой sizeof(type) для всех встроенных типов и «обычных указателей» равен 1, но число бит и диапазоны значений для типов разные.
ПК>Нет, такая система невозможна.
ПК>3.9.1 Fundamental types ПК>1 For character types, all bits of the object representation participate in the value representation. For unsigned character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types.
ПК>Кроме того,
ПК>2 “signed char”, “short int”, “int”, and “long int.” In this list, each type provides at least as much storage as those preceding it in the list.
ПК>Таким образом, т.к. все биты представления char являются значащими, и short представляет не меньший диапазон значений, чем char, а также sizeof(short) == 1, все биты short также являются значащими. То же по индукции для остальных целочисленных типов.
Пункта 3.9\4 достаточно, чтобы понять, что система, где sizeof всех встроенных типов 1, а число бит разное, невозможна. Размеры всех встроенных типов могут быть одинаковы, но тогда число бит в object representation char должно быть столько же, сколько в long и в любом другом типе и все должны быть значимы.
ПК>>>Прерывания, так же как и (почти?) любая асинхронность, являются понятиями за пределами стандарта. Поэтому, фактически, любое асинхронное взаимодействие само по себе автоматом приводит к undefined behavior.
D>>Сигналы не являются понятиями за пределами Стандарта, а они асинхронны.
ПК>Именно поэтому я и написал "почти". Но стандарт, фактически, не определяет семантику сигналов, а просто констатирует их наличие. Однако, если в твоем примере заменить прерывание на сигнал, то:
ПК>[i]9 When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects with type other than volatile sig_atomic_t are unspecified, and the value of any object not of volatile sig_atomic_t that is modified by the handler becomes undefined.[/b]
Обработчик сигнала ничего менять не будет, просто сам факт возникновения сигнала может сорвать запись в ячейку, если не предпринимать особых усилий. Суть моего примера в том, что Стандарт должен подталкивать реализации к более безопасным программа с определенным поведением, а не говорить, что если где-то что-то может привести к неопределенному поведению, то на всех системах будет неопределенное поведение.
ПК>То, что стандарт не накладывает никаких ограничений на поведение программы, не означает, что она ill-formed. Ill-formed в общем означает, что компилятор должен выдавать диагностику (за исключением ODR, что оговорено отдельно).
Написав ill-formed я погорячился. Вообще любая реализация может выполнять программы содержащие ошибки, да она не будет полностью конформной Стандарту, но так как таких реализаций вообще не существует (на сколько мне известно), то это не имеет значения.
Вообще вопрос, видимо, перерос все разумные рамки, то его можно переместить в философию.
Здравствуйте dupamid, Вы писали:
ПК>>если в твоем примере заменить прерывание на сигнал, то:
ПК>>9 When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects with type other than volatile sig_atomic_t are unspecified, and the value of any object not of volatile sig_atomic_t that is modified by the handler becomes undefined.
D>Обработчик сигнала ничего менять не будет, просто сам факт возникновения сигнала может сорвать запись в ячейку, если не предпринимать особых усилий. Суть моего примера в том, что Стандарт должен подталкивать реализации к более безопасным программа с определенным поведением,
Именно это он и делает, где это возможно. Например, в случае сигналов, результат как раз unspecified. Более ничего гарантировать нельзя.
D>а не говорить, что если где-то что-то может привести к неопределенному поведению, то на всех системах будет неопределенное поведение.
А толку от таких требований стандарта? Так хоть заранее известно, что данная "фича" непереносима с системы на систему, а в противном случае в стандарте поведение будет определено, а в реальности — нет. Стандарт не предназначен для "дисциплинирования" реализаторов, он просто-напросто закрепляет общие моменты существующих реализаций. Иначе стандарт рискует попасть в положение игнорируемого реализаторами документа. Что и без того наблюдается в некоторых моментах, где комитет позволил себе "новаторство": export, exception specifications etc.
Таким образом, раз на части платформ поведение неопределено, то есть два выбора: либо объявить поведение undefined, либо объявить поведение unspecified, а реализации на этих платформах несоответствующими стандарту. Учитывая, что хрен (unspecified) редьки (undefined) не слаще и то, что в голосовании принимают участие и разработчики этих реализаций, выбор почти очевиден.
Кроме того, для меня, как и для многих других пользователей, выгоднее иметь более агрессивную оптимизацию со стороны компилятора, чем такую "фичу". А те, кто готовы платить за надежность ценой производительности, пусть используют обертки вокруг встроенных типов — там все в этом отношении определено.
D>Написав ill-formed я погорячился. Вообще любая реализация может выполнять программы содержащие ошибки, да она не будет полностью конформной Стандарту, но так как таких реализаций вообще не существует (на сколько мне известно), то это не имеет значения.
Любая реализация может скомпилировать программу, приводящую к undefined behavior, и при этом оставаться полностью соответствующей стандарту.
D>Вообще вопрос, видимо, перерос все разумные рамки, то его можно переместить в философию.
Ну, причем здесь "философия"? Вопрос-то чисто технический, по C++. Так что никуда ничего переносить не надо. А размеры... Так, непростой вопрос, вот обсуждение и затянулось. Главное, оставить его в "технических" рамках, не прибегая к "философским" аргументам
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте Павел Кузнецов, Вы писали:
D>>а не говорить, что если где-то что-то может привести к неопределенному поведению, то на всех системах будет неопределенное поведение.
ПК>А толку от таких требований стандарта? Так хоть заранее известно, что данная "фича" непереносима с системы на систему, а в противном случае в стандарте поведение будет определено, а в реальности — нет. Стандарт не предназначен для "дисциплинирования" реализаторов, он просто-напросто закрепляет общие моменты существующих реализаций. Иначе стандарт рискует попасть в положение игнорируемого реализаторами документа. Что и без того наблюдается в некоторых моментах, где комитет позволил себе "новаторство": export, exception specifications etc.
Данная фича переносима в том смысле, что можно ее записать так чтобы в выражении появилась точка следования и тогда все должно быть точно определено в любом случае (может быть будет нужна дополнительная временная переменная, а может быть она будет нужна в любом случае при генерации).
Стандарт не просто закрепляет общие моменты, но и вырабатывает новые свойства языка (что нас в скором будущем обязательно ждет). Язык С++ разрабатывался достаточно давно и ему не хватает некоторых свойств, и если он не хочет умереть и остаться одним языком, а не тучей реализаций, то должен развиваться. Так что я не согласен с таким констатирующим свойством Стандарта, в начале это было нужно, чтобы свести все реализации вместе, но сейчас он должен предлагать новые свойства и дисциплинировать реализации (хороший термин). Это не только мои мысли, но и соображения Страуструпа на будущее С++.
ПК>Таким образом, раз на части платформ поведение неопределено, то есть два выбора: либо объявить поведение undefined, либо объявить поведение unspecified, а реализации на этих платформах несоответствующими стандарту. Учитывая, что хрен (unspecified) редьки (undefined) не слаще и то, что в голосовании принимают участие и разработчики этих реализаций, выбор почти очевиден.
ПК>Кроме того, для меня, как и для многих других пользователей, выгоднее иметь более агрессивную оптимизацию со стороны компилятора, чем такую "фичу". А те, кто готовы платить за надежность ценой производительности, пусть используют обертки вокруг встроенных типов — там все в этом отношении определено.
Обертки намного более медленные. Как известно, какая бы не была агрессивная оптимизация компилятора, она обычно дает только небольшой прирост производительности по сравнению с выбором других алгоритмов внутри программы. Оптимизация почти никогда (стараюсь быть менее категоричным) не может сделать из алгоритма O(n) алгоритм O(log(n)). Здесь же проигрыш в эффективности на большинстве систем вообще никакой, и незначительный на системах, где потребуется специальная поддержка.
D>>Написав ill-formed я погорячился. Вообще любая реализация может выполнять программы содержащие ошибки, да она не будет полностью конформной Стандарту, но так как таких реализаций вообще не существует (на сколько мне известно), то это не имеет значения.
ПК>Любая реализация может скомпилировать программу, приводящую к undefined behavior, и при этом оставаться полностью соответствующей стандарту.
Здесь возникает вопрос о теоретической возможности существования реализации полностью соответствующей Стандарту. Речь идет, конечно же, не о рекламных заявлениях, которые часто мало чему соответствуют, а о реальной жизни, Стандарт вещь достаточно противоречивая и содержит ошибки, опечатки и т.д.
Здравствуйте dupamid, Вы писали:
D>Данная фича переносима в том смысле, что можно ее записать так чтобы в выражении появилась точка следования и тогда все должно быть точно определено в любом случае (может быть будет нужна дополнительная временная переменная, а может быть она будет нужна в любом случае при генерации).
Ты о чем? Если между модификациями появится точка следования, то поведение полностью определено и безо всяких изменений в текущей версии стандарта. Пользуйся.
D>Стандарт не просто закрепляет общие моменты, но и вырабатывает новые свойства языка (что нас в скором будущем обязательно ждет).
Это, интересно, откуда такие сведения? Все известные мне высказывания членов комитета стандартизации говорят как раз об обратном.
D>Язык С++ разрабатывался достаточно давно и ему не хватает некоторых свойств, и если он не хочет умереть и остаться одним языком, а не тучей реализаций, то должен развиваться.
Именно для того, чтобы реализации могли бы догнать оторвавшийся от жизни стандарт, в ближайшей ревизии запланировано минимизировать изменения/дополнения языка, и, в основном, планируется ограничиться модификацией спецификаций стандартных библиотек. Более того, в дальнейшем, в целях избежания казусов, подобных export или exception specification, запланировано включать в стандарт только вещи, прошедшие проверку, желательно в качестве расширения хотя бы на одной из существующих реализаций.
D>Так что я не согласен с таким констатирующим свойством Стандарта, в начале это было нужно, чтобы свести все реализации вместе, но сейчас он должен предлагать новые свойства и дисциплинировать реализации (хороший термин).
Ты, вероятно, не вполне ясно представляешь себе процесс стандартизации. Для того, чтобы что-то попало в стандарт, кто-то должен проделать тяжелую часть работы по подготовке спецификации и экспериментальной проверке своей идеи и убедить остальных, что затраты на внедрение предлагаемого решения окупаются выгодами от его использования в дальнейшем. Учитывая оправданный скепсис к любым нововведениям, у тебя фактически не будет шансов убедить членов комитета без наличия reference implementation.
Если ты действительно хочешь, чтобы модификация скалярных объектов между соседними точками следования не приводила к неопределенному поведению — пожалуйста. Самым легким способом достичь этого является примерно следующий (использован текст сообщения Daniel Miller <daniel.miller@tellabs.com> из comp.std.c++):
1) Реализуй подобный компилятор (или измени существующий) сам, или вместе с теми, кто разделяет твою точку зрения. Если ты сам не можешь сделать этого, и не можешь найти никого, кто согласен тебе в этом помочь, то, считай, что ничего из твоей идеи не выгорит — если ты не можешь убедить одного разработчика, как ты надеешься убедить целый скептически настроенный комитет? Кроме того, без reference implementation изменения языка не рассматриваются. Учти, что то, что ты делаешь на этой стадии может иметь существеннейшие последствия для всех реализаций, поэтому твоей задачей является учесть все подводные камни, встречающиеся при реализации этой "фичи". Это как раз и есть то время, когда приветствуются изобретательность, новаторство, исследования и т.п. В течение дальнейших шагов изменения вносить все тяжелее и тяжелее, т.к. все большее кол-во людей зависят от деталей спецификации. Если что-то будет упущено на этом этапе, идея будет все больше закрепляться в ущербном виде.
2) Если ты преуспел на первом этапе — хорошо. Теперь настало время получить результаты более широкого тестирования этой идеи путем включения ранее тестовой версии в распространяемую версию какого-нибудь компилятора. Это, естественно, автоматически означает некоторое закрепление спецификации "фичи", так как она со временем тиражируется все шире и шире.
3) После этого, пусть положительный опыт от использования "фичи" во все большем количестве реализаций убеждает людей все больше и больше в ее полезности. И наконец...
4) Наконец, очередной шаг стандартизации неминуемо подхватит "фичу" как образец существующей практики и она уже будет зацементирована в тексте стандарта.
D>Это не только мои мысли, но и соображения Страуструпа на будущее С++.
Цитату, пожалуйста. Такого он, насколько я знаю, не говорил, напротив, по крайней мере, в тех интервью, которые мне встречались, он высказывался в духе минимизации core language additions в ближайшей ревизии стандарта.
ПК>>для меня, как и для многих других пользователей, выгоднее иметь более агрессивную оптимизацию со стороны компилятора, чем такую "фичу". А те, кто готовы платить за надежность ценой производительности, пусть используют обертки вокруг встроенных типов — там все в этом отношении определено.
D>Обертки намного более медленные.
Ты сам себе противоречишь. Обертки медленнее именно из-за наличия точек следования. Если тебя это устраивает — пользуйся ими, если нет — будь готов мириться с некоторыми нюансами использования скалярных типов. Кроме точек следования, ничто не заставляет компилятор трактовать простые обертки по-другому в отношении оптимизации, чем скалярные типы. (1)
D>Как известно, какая бы не была агрессивная оптимизация компилятора, она обычно дает только небольшой прирост производительности по сравнению с выбором других алгоритмов внутри программы.
Да?.. Например, на некоторых задачах, использование более агрессивно оптимизирующего Intel C++ Compiler по сравнению с Visual C++ 6.0 приводило к двойному ускорению программы в целом.
D>Оптимизация почти никогда (стараюсь быть менее категоричным) не может сделать из алгоритма O(n) алгоритм O(log(n)).
Ну и что? Замедление фрагмента программы, отвечающего за отрисовку картинки в игре на 50% запросто может приводить к плюс-минус соответствующему уменьшению fps, что уж никак нельзя назвать незначительным.
D>Здесь же проигрыш в эффективности на большинстве систем вообще никакой, и незначительный на системах, где потребуется специальная поддержка.
Без достаточного обоснования выглядит как достаточно поверхностное суждение. См. (1). Если ты хочешь убедить меня в том, что определенность неоднократной модификации скалярных объектов между соседними точками следования действительно не приводит к существенному снижению способности компилятора к оптимизации — пожалуйста.
1) Опиши, к какому результату, согласно предлагаемой спецификации, должно приводить выполнение выражения "++*p + ++*q" в случае, если p и q указывают на один и тот же объект.
2) Напиши, как по-твоему в таком случае, должен быть сгенерирован следующий фрагмент кода:
int foo(int* p, int *q)
{
return ++*p + ++*q;
}
3) Сравни предложенный результат с, например, таким:
Здравствуйте dupamid, Вы писали:
D> Core Language Ideas D>• Improve consistency and portability: D> § Unify lookup between functions & functors D> § Minimize “implementation-defined” & “undefined”
D>• Provide guarantees for general concurrency D> § Atomicity of selected operations D> § Signal-handling requirements
D>• Remove language impediments to use of D> garbage-collection for memory management
Снова-таки, ни слова в подтверждение твоего высказывания о том, что "Стандарт не просто закрепляет общие моменты, но и вырабатывает новые свойства языка". Minimize “implementation-defined” & “undefined” ни в коей мере не является аргументом в поддержку твоих высказываний о точках следования.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте dupamid, Вы писали:
ПК>>Это, интересно, откуда такие сведения? Все известные мне высказывания членов комитета стандартизации говорят как раз об обратном.
D>Пока я пишу подробный ответ по остальным вопросам, посмотри ссылочку <...>
Это все очень старое и очень известное.
1) Там ничего не говорится о том, что комитет будет источником новшеств.
2) Подтверждается, что "No major new language features are needed", "Avoid major (language) extensions".
3) Все предлагаемые core language ideas сводятся к исправлению "ляпов" ("identical lookup for functions and function objects", "Minimize “implementation dependent/undefined/…”"), узакониванию существующей практики ("Typedef templates, maybe typeof()") и некоторым "политическим" шагам типа совместимости с C, о чем в CUJ было напечатано три статьи.
4) "Encourage implementers to introduce new features in a common defined order" дополнительно подчеркивает, что источником новшеств должны служить существующие реализации.
"Evolution WG Proposal Skeleton" (N1364) специально построен так, что внесение какого-либо предложения по добавлению или изменению чего-либо в языке/стандартных библиотеках, требует reference implementation. Проще говоря, если твои источники говорят тебе, что комитет будет что-то изобретать, то они просто не верны. Для подтверждения полистай comp.std.c++. Это не означает, что в новой версии стандарта не будет ничего нового. Однако, новшества должны быть обязательно опробованы на практике, что для core language additions означает введение их в какой-либо реализации в качестве расширения до того.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте Павел Кузнецов, Вы писали:
ПК>Снова-таки, ни слова в подтверждение твоего высказывания о том, что "Стандарт не просто закрепляет общие моменты, но и вырабатывает новые свойства языка".
Комитет уже сейчас обсуждает новые свойства языка (я это знаю абсолютно точно), которые пока никто в С++ не реализовал, это новые идеи, которые только вырабатываются, так что Стандарт не просто закрепляет общие моменты.
ПК>Minimize “implementation-defined” & “undefined” ни в коей мере не является аргументом в поддержку твоих высказываний о точках следования.
Сокращение implementation-defined & undefined это именно то, о чем я говорю, Стандарт должен больше требовать от реализаций, у нее должно быть меньше свобод, Стандарт должен гарантировать выполнение программ.
Здравствуйте dupamid, Вы писали:
D>Комитет уже сейчас обсуждает новые свойства языка (я это знаю абсолютно точно), которые пока никто в С++ не реализовал, это новые идеи, которые только вырабатываются, так что Стандарт не просто закрепляет общие моменты.
Например? Мне кажется ты путаешь понятия "комитет" и "члены комитета".
ПК>>Minimize “implementation-defined” & “undefined” ни в коей мере не является аргументом в поддержку твоих высказываний о точках следования.
D>Сокращение implementation-defined & undefined это именно то, о чем я говорю,
Но, это не означает, что упомянутые “implementation-defined” & “undefined” включают в себя сокращение undefined в случае неоднократной модификации скалярных объектов между соседними точками следования До тех пор, пока не будет показано обратное, аргументом в пользу твоих рассуждений это не станет.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Мои сведенья основываются на чтении внутренней переписки комитета, т.е. из самых первых рук. Я вижу, как выдвигаются предложения по новым свойствам, вижу насколько они сырые, и как оттачиваются. Это предложения — реализациями там пока и не пахнет. Может быть, перед введением в Стандарт они будут где-то обкатываться, но предложения о новых свойствах поступают изнутри комитета, а не снаружи, в частности и от самого Страуструпа.
Здравствуйте dupamid, Вы писали:
D>Это похоже на разговор слепого с глухим.
На твое усмотрение.
D>Мои сведенья основываются на чтении внутренней переписки комитета, т.е. из самых первых рук.
Это и есть то, что я подразумевал под смешением понятий "комитет" и "члены комитета".
D>Я вижу, как выдвигаются предложения по новым свойствам <...>
Тем не менее, они не войдут в стандарт, пока не будет их практического подтверждения. В случае core language additions это означает почти обязаетельную реализацию в одном из компиляторов с последующей более-менее широкой апробацией.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Intel’овский процессор выбран крайне не удачно, так как на нем гарантируется, что при попытке одновременной записи в ячейку, операции будут выполняются последовательно. Поэтому ни один из приводимых примеров для процессоров intel’не приводит к аппаратным исключениями или другому неопределенному поведению. Вернемся к моим начальным примерам:
[ccode]
#include <iostream>
using namespace std;
int i, j, k;
int main()
{
i = i = 1;
cout << "i == " << i << endl;
i = ++i;
cout << "i == " << i << endl;
i = (i = j + k) + (i = j — k);
cout << "i == " << i << endl;
}
[\ccode]
Вывод:
[ccode]
i == 1
i == 2
i == 0
[\ccode]
Приведите пример реализации, на которой вывод этой программы будет другим или произойдет какое-нибудь неопределенное поведение. Теоретически это возможно, но для процессов intel такой компилятор, видимо, найти вообще не удастся.
Теперь другой пример:
[ccode]
#include <iostream>
using namespace std;
int f(int* p, int* q)
{
return ++*p + ++*q;
}
int main()
{
int i = 0;
int j = f(&i, &i);
cout << "i == " << i << "\tj == " << j << endl;
}
[\ccode]
На MSVC.NET вывод:
[ccode]
i == 2 j == 4
[\ccode]
Здесь для intel возможны варианты в зависимости от компилятора и настроек, i может принимать два значения 1 и 2, j 2 и 4, остальные варианты, кажется, не возможны. Но опять таки никакого неопределенного поведения, максимум это можно назвать неспецифицированным поведением. Подчеркиваю еще раз теоретически могут быть другие варианты. Опять нужны примеры реализаций где переменные i и j будут принимать другие значения и/или происходить аппаратные исключения и т.д.
Так что на процессорах intel никаких накладных расходов, на то чтобы сделать поведение программы вместо неопределенным неспецифицированным нет. Если говориться, что существуют процессоры где есть С++ и накладные расходы на неспецифицированное поведение настолько велики, что Стандарт не может их потребовать, то прошу приводить конкретные примеры иначе спор становиться бессмысленным.
Что касается комитета и его членов, то комитет это его члены. Более того некоторые члены комитета, бОльшие члены, чем другие, и личный фактор имеет большое значение в комитете по стандартизации С++.
Что же касается широкой апробации новых свойств С++ и обязательного наличия реализации, то EDG что-то реализует и можно сказать, что есть реализация, так что проблемы с апробацией новых свойств языка в рамках комитета по Стандартизации я не вижу. Более того требование наличия реализация может быть просто вредно, так как если какая-то крупная компания типа MS что-то реализует в своем компиляторе, то это станет стандартом де-факто, хотя это может быть сделано совсем не удачно, но изменить уже ничего будет нельзя. Так что лучше все хорошо продумать чем воплощать в продуктах, которые станут стандартом де-факто, а потом рвать на себе волосы, что получилось не самым лучшим образом и не переноситься на другие платформы.
Здравствуйте dupamid, Вы писали:
D>Intel’овский процессор выбран крайне не удачно, так как на нем гарантируется, что при попытке одновременной записи в ячейку, операции будут выполняются последовательно. Поэтому ни один из приводимых примеров для процессоров intel’не приводит к аппаратным исключениями или другому неопределенному поведению.
В какой-то момент мне показалось, что ты вообще захотел, чтобы поведение в подобных случаях было определенным (не unspecified и не undefined). Мои аргументы в соответствующем сообщении относились именно к этому, можешь их в таком случае игнорировать. Я сейчас пересмотрел тред и увидел, что не вполне правильно тебя понял. Хорошо, "продолжаем разговор".
D>Вернемся к моим начальным примерам:
<... простые примеры неоднократной модификации скалярных объектов между соседними точками следования ...> D>Приведите пример реализации, на которой вывод этой программы будет другим или произойдет какое-нибудь неопределенное поведение. Теоретически это возможно, но для процессов intel такой компилятор, видимо, найти вообще не удастся.
Действительно, для подобных простых примеров, вероятность получить что-нибудь уж очень неожиданное на скалярных платформах не велика. Отчасти это происходит из-за того, что компиляторы "понимают" подобные простые случаи.
Compaq C Compiler умеет выдавать сообщения вида:
cc: Warning: miscplay.c, line 384: In this statement, the expression
"*dest++=(unsigned char)(((int)*((signed char ...)(src++))+(int)*((signed char ...)(src++)))/2)"
modifies the variable "src" more than once without an intervening sequence point.
This behavior is undefined. (undefvarmod)
*dest++ = (unsigned char)(((int)*((signed char *)(src++)) +
----^
Говорят, что GCC начиная с версии 3.0 с ключом -Wall тоже делает нечто подобное.
D>Теперь другой пример:
<... ++*p + ++*q ...> D>Здесь для intel возможны варианты в зависимости от компилятора и настроек, i может принимать два значения 1 и 2, j 2 и 4, остальные варианты, кажется, не возможны. Но опять таки никакого неопределенного поведения, максимум это можно назвать неспецифицированным поведением. Подчеркиваю еще раз теоретически могут быть другие варианты. Опять нужны примеры реализаций где переменные i и j будут принимать другие значения
Ну, насчет других вариантов — легко, только на всякий случай перепишем пример так, чтобы компилятор "не знал" при генерации функции, что p и q указывают на один и тот же объект:
`foo.cpp'
int foo(int* p, int *q)
{
return ++*p + ++*q;
}
`main.cpp'
#include <iostream>
int foo(int*, int*);
int main()
{
int i = 0;
int j = foo(&i, &i);
std::cout << "i == " << i << "\tj == " << j << std::endl;
}
>icl /Ox main.cpp foo.cpp
Intel(R) C++ Compiler for 32-bit applications, Version 5.0.1 Build 010727Z
Copyright (C) 1985-2001 Intel Corporation. All rights reserved.
>main.exe
i == 2 j == 3
А если несколько усложнить ситуацию, то получим еще более различные результаты:
#include <iostream>
int foo(int*, int*, int*, int*);
int main()
{
int i = 0;
int j = foo(&i, &i, &i, &i);
std::cout << "i == " << int(i) << "\tj == " << int(j) << std::endl;
}
>icl /Ox main.cpp foo.cpp
Intel(R) C++ Compiler for 32-bit applications, Version 5.0.1 Build 010727Z
Copyright (C) 1985-2001 Intel Corporation. All rights reserved.
>main.exe
i == 22 j == 43
>cl /Ox /GX main.cpp foo.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.00.9466 for 80x86
Copyright (C) Microsoft Corporation 1984-2001. All rights reserved.
>main.exe
i == 26 j == 52
Кроме того, если отойти слегка в сторону, например, сообщалось, что SCO Optimizing C compiler на одной из платформ для:
int a = 123, b = 7654;
a ^= b ^= a ^= b;
генерирует код, устанавливающий b в 123 и a в 0.
D>и/или происходить аппаратные исключения и т.д.
Любая векторная платформа, на которой не разрешена одновременная запись в ячейку. В принципе, в свое время в comp.lang.c++.moderated сообщалось, что есть устройства, имеющие (кросс-)компилятор C, для которых запись в одну и ту же ячейку не может осуществляться без задержки. В противном случае, происходит что-нибудь "страшное".
D>Если говориться, что существуют процессоры где есть С++ и накладные расходы на неспецифицированное поведение настолько велики, что Стандарт не может их потребовать, то прошу приводить конкретные примеры иначе спор становиться бессмысленным.
(Почти?) любые векторные суперкомпьютеры или большинство (?) платформ, поддерживающих параллельное выполнение инструкций с записью в память. Например, тот же Cray. Вообще, одна из тенденций даже в современных "настольных" процессорах — поддержка параллельных вычислений (IA-64/EPIC/VLIW, Motorola G4+, G5). При "по-настоящему" параллельном выполнении, описывать возможные варианты результатов выражений с множественной модификацией скалярных объектов между соседними точками следования уже смысла не имеет, если вообще возможно, не говоря уж о дальнейшем поведении программы, если не все объектные представления являются допустимыми. Простейшие примеры: для float это может привести к совершенно неожиданному signaling NaN при вычислении совершенно "безобидного" выражения, для указателей — к null pointer в самом неожиданном месте и т.д. Короче, undefined behavior.
D>Что касается комитета и его членов, то комитет это его члены.
Не вполне. Каждый из членов комитета может на досуге "играться" с новыми/интересными "фичами", обсуждать свои "игрушки" с другими и т.д. Но когда дело дойдет до включения "фичи" в стандарт, кто-то должен будет предоставить reference implementation и результаты практического использования. В данный момент, насколько мне известно, комитет в целом решительно против введения в стандарт неопробованных "фич".
D>Более того некоторые члены комитета, бОльшие члены, чем другие, и личный фактор имеет большое значение в комитете по стандартизации С++.
Естественно, но это никак не влияет на высказанный тезис.
D>требование наличия реализация может быть просто вредно, так как если какая-то крупная компания типа MS что-то реализует в своем компиляторе, то это станет стандартом де-факто, хотя это может быть сделано совсем не удачно, но изменить уже ничего будет нельзя.
Это все равно лучше, чем включить что-то вообще нигде не реализованное в стандарт, т.е. менее продуманное, практические последствия чего полностью изучены быть не могут и т.д.
D>Так что лучше все хорошо продумать чем воплощать
Это не выбор: или — или. Должно быть: и — и.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте Павел Кузнецов, Вы писали:
ПК>Действительно, для подобных простых примеров, вероятность получить что-нибудь уж очень неожиданное на скалярных платформах не велика. Отчасти это происходит из-за того, что компиляторы "понимают" подобные простые случаи.
Она не просто «не велика», с практической точки зрения можно сказать, что она равна нулю, так как во многих случаях такая поддержка есть на аппаратном уровне и компиляторам вообще ничего специального делать не надо.
ПК>Compaq C Compiler умеет выдавать сообщения вида:
Это, насколько я понимаю, компилятор С, я же говорю о том, что С++ обладает сложной, часто скрытой семантикой и говорю, что для С++ можно и нужно сделать поведение в этом случае не специфицированным, для С его можно оставить не определенным. Тут мне могут сказать, что это будет очередная несовместимость С и С++, отвечу сразу, при наличии всех остальных несовместимостей это уже ничего не меняет. С язык более низкого уровня и может быть менее безопасным.
D>>Теперь другой пример: ПК><... ++*p + ++*q ...> D>>Здесь для intel возможны варианты в зависимости от компилятора и настроек, i может принимать два значения 1 и 2, j 2 и 4, остальные варианты, кажется, не возможны. Но опять таки никакого неопределенного поведения, максимум это можно назвать неспецифицированным поведением. Подчеркиваю еще раз теоретически могут быть другие варианты. Опять нужны примеры реализаций где переменные i и j будут принимать другие значения
ПК>Ну, насчет других вариантов — легко, только на всякий случай перепишем пример так, чтобы компилятор "не знал" при генерации функции, что p и q указывают на один и тот же объект
ПК>i == 2 j == 3[/code]
Через пять минут, после того как я послал пример, понял, что возможен вариант «j == 3», но это ничего не меняет, все равно количество вариантов для этого примера сильно ограничено.
ПК>А если несколько усложнить ситуацию, то получим еще более различные результаты:
Это все равно хорошо (я бы сказал великолепно) укладывается в не специфицированное поведение, в случае с порядком вычисления аргументов функции могут быть и еще более тяжелые случаи.
ПК>Кроме того, если отойти слегка в сторону, например, сообщалось, что SCO Optimizing C compiler на одной из платформ для: ПК>
ПК>int a = 123, b = 7654;
ПК>a ^= b ^= a ^= b;
ПК>генерирует код, устанавливающий b в 123 и a в 0.
Опять компилятор С, и опять дело только в значении. Я слышал множество споров про всякие задачки с 5 плюсами, но никогда не слышал, чтобы они приводили к катастрофическим последствиям, всегда дело было только в значении.
ПК>Любая векторная платформа, на которой не разрешена одновременная запись в ячейку. В принципе, в свое время в comp.lang.c++.moderated сообщалось, что есть устройства, имеющие (кросс-)компилятор C, для которых запись в одну и ту же ячейку не может осуществляться без задержки. В противном случае, происходит что-нибудь "страшное".
Вот я хотел бы увидеть такую систему, где авторы компилятора пошли на такой шаг, если таких систем нет, то как можно говорить, что Стандарт только фиксирует текущее положение дел и не вносит ничего нового.
D>>Если говориться, что существуют процессоры где есть С++ и накладные расходы на неспецифицированное поведение настолько велики, что Стандарт не может их потребовать, то прошу приводить конкретные примеры иначе спор становиться бессмысленным.
ПК>(Почти?) любые векторные суперкомпьютеры или большинство (?) платформ, поддерживающих параллельное выполнение инструкций с записью в память. Например, тот же Cray. Вообще, одна из тенденций даже в современных "настольных" процессорах — поддержка параллельных вычислений (IA-64/EPIC/VLIW, Motorola G4+, G5). При "по-настоящему" параллельном выполнении, описывать возможные варианты результатов выражений с множественной модификацией скалярных объектов между соседними точками следования уже смысла не имеет, если вообще возможно, не говоря уж о дальнейшем поведении программы, если не все объектные представления являются допустимыми. Простейшие примеры: для float это может привести к совершенно неожиданному signaling NaN при вычислении совершенно "безобидного" выражения, для указателей — к null pointer в самом неожиданном месте и т.д. Короче, undefined behavior.
Вот я и хочу увидеть хоть одну систему, где действительно происходит undefined behavior, я слышу только слова о теоретический возможности. Реальные примеры есть?
D>>Что касается комитета и его членов, то комитет это его члены.
ПК>Не вполне. Каждый из членов комитета может на досуге "играться" с новыми/интересными "фичами", обсуждать свои "игрушки" с другими и т.д. Но когда дело дойдет до включения "фичи" в стандарт, кто-то должен будет предоставить reference implementation и результаты практического использования. В данный момент, насколько мне известно, комитет в целом решительно против введения в стандарт неопробованных "фич".
Что значит не опробованные фичи? Значения по умолчанию для параметров шаблонных функций (Item 226 Default template arguments for function templates), это как опробованная фича или нет, или, например, (Default arguments in template template-parameters 184).
D>>требование наличия реализация может быть просто вредно, так как если какая-то крупная компания типа MS что-то реализует в своем компиляторе, то это станет стандартом де-факто, хотя это может быть сделано совсем не удачно, но изменить уже ничего будет нельзя. Так что лучше все хорошо продумать, чем воплощать
ПК>Это не выбор: или — или. Должно быть: и — и.
Я не утверждаю, что такой выбор это хорошо, но реальная жизнь такова, что часто вопрос стоит именно так «или – или».
Здравствуйте Павел Кузнецов, Вы писали:
ПК>Если ты думаешь, что возможные варианты сводятся к i == [1, 2], j == [2, 3, 4], то я с легкостью нарисую тебе логичную последовательность инструкций, приводящую к другим результатам. Если ты предполагаешь другой диапазон — скажи, и я снова-таки предоставлю тебе логичную последовательность инструкций, не только приводящую к другому результату
Интересно как такой кусок код “++*p + ++*q” сам по себе, без дополнительных циклов вокруг, привел бы к значению, скажем 100. Как функция f будет приводить к другим значениям:
int f(int* p, int* q)
{
return ++*p + ++*q;
}
[\ccode]
ПК> но и влияющую на дальнейшее поведение программы, вплоть до нарушения семантики основных инструкций типа if. Учти, что следует рассматривать вычисления подобных выражений в контексте, где компилятору, например, может быть выгодно использовать результаты предыдущих вычислений и т.п.:
ПК>[ccode]
ПК>void foo(int* p, int* q)
ПК>{
ПК> *p = ++*q;
ПК> if (*p != *q)
ПК> launch_rocket("SS-20");
ПК>}
Точно то же самое можно сказать про не специфицированное поведение:
[ccode]
int i;
int f(int j)
{
return i = j;
}
void g(int, int);
void foo()
{
g(f(1), f(2));
if(i == 2) launch_rocket("SS-20");
}
[\ccode]
В зависимости от порядка вычисления аргументов будем запускать ракету – типичное не специфицированное поведение и ошибка в логике программы. Вообще, если этот кусок кода не из игрушки, то он проходит тестирование и сертификацию, скорее всего вместе с процессором, инструментальным компилятором и компилятором на котором собирался инструментальный компилятор и т.д.
D>>Вот я хотел бы увидеть такую систему
ПК>Мне тоже интересно, но лично не встречал. Насколько я понимаю, всякие контроллеры и т.п. вспомогательные железяки. Однако, мы с тобой не специалисты по embended системам и микроконтроллерам, а при написании стандарта таковые присутствовали. В любом случае, это не столь существенно, т.к. описывать варианты поведения даже без этих проблем не представляется нужным, а то и возможным.
Я знаю несколько систем, где возможно параллельное вычисление инструкций, одна из них встроенная, но ни одна из этих систем не допустит неконтролируемой записи в одну и ту же ячейку. Подобное поведение контролируется на аппаратном уровне.
ПК>Сколько угодно: undefined behavior сейчас происходит на всех системах. Undefined behavior это не "что-то страшное", а поведение, не определенное стандартом. Посмотри на другие источники undefined behavior: ПК>Имхо, множественная модификация скалярных объектов между соседними точками следования вполне неплохо вписывается в общую схему undefined behavior: те места, где стандарт по тем или иным причинам не вообще не ограничивает разработчиков компилятора или где предполагается возможность, что данные будут находиться в непредсказуемом состоянии. ПК>Unspecified же относится к более ясным вещам, где есть определенный выбор вариантов:
Примеры достаточно убедительны, но вот высказывание Jerry Schwarz как раз по подобному поводу: «The value of this expression has been an issue for almost as long as there have been C compilers. My recollection is that Ritchie's original C compiler compiled this in "the obvious way" to give 5, but that Steve Johnson's pcc had code generating technology that always resulted in 6 (or maybe always resulted in 4, or maybe was platform specific) and was a common compiler at the time C was standardized. It was accepted at the time that the value was undefined (or maybe unspecified — in the early days we often didn't make that distinction).»
Это говорит только о том, что часть решений между undefined и unspecified просто сложилась исторически и веских оснований под собой не имеет, я не утверждаю что это тот самый случай, но кто знает...
Здравствуйте dupamid, Вы писали:
D>Интересно как такой кусок код “++*p + ++*q” сам по себе, без дополнительных циклов вокруг, привел бы к значению, скажем 100. Как функция f будет приводить к другим значениям:
<... ф-я int f(int* p, int* q), содержащая только return ++*p + ++*q; ...>
Честно? Мне уже просто лень выписывать возможные последовательности инструкций. Важно другое: в общем случае может быть все, что угодно.
ПК>> но и влияющую на дальнейшее поведение программы, вплоть до нарушения семантики основных инструкций типа if. Учти, что следует рассматривать вычисления подобных выражений в контексте, где компилятору, например, может быть выгодно использовать результаты предыдущих вычислений и т.п.: ПК>>
D>Точно то же самое можно сказать про не специфицированное поведение:
<...> D>В зависимости от порядка вычисления аргументов будем запускать ракету – типичное не специфицированное поведение и ошибка в логике программы.
Совсем не то же. Дело не в том, что будет запускаться ракета, а в том, что конструкция *p != *q "по идее" для p == q всегда должна возвращать false. Если она возвращает true, что это, как не проявление undefined behavior?
D>Примеры достаточно убедительны, но вот высказывание Jerry Schwarz как раз по подобному поводу: D>«The value of this expression has been an issue for almost as long as there have been C compilers. My recollection is that Ritchie's original C compiler compiled this in "the obvious way" to give 5, but that Steve Johnson's pcc had code generating technology that always resulted in 6 (or maybe always resulted in 4, or maybe was platform specific) and was a common compiler at the time C was standardized. It was accepted at the time that the value was undefined (or maybe unspecified — in the early days we often didn't make that distinction).» D>Это говорит только о том, что часть решений между undefined и unspecified просто сложилась исторически
Нет, это говорит о том, что в какое-то время не слишком задумывались над разницей между unspecified и undefined. Например, в книге K&R, если я правильно помню, этот случай помечен именно как unspecified. При стандартизации (на самом деле еще раньше) всем этим случаям было уделено пристальнейшее внимание. Но это вовсе не означает, что до стандартизации этот случай приводил "всего лишь" к unspecified behavior. Это примерно как говорить, что до Эйнштейна, Лоренца и компании проблем с Ньютоновской механикой не наблюдалось.
<< J 1.0 alpha 4 >>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен