Ошибка в вычислениях адресов у GCC ?

Sun, 30 Jan 2005 17:55:13 +0300 Alex Mogilnikov wrote to Harry Zhurov:

HZ>> И где тут английским по белому написано, что _не_ _определено_? HZ>> Где слово undefined? Тут вообще не рассматривается обсуждаемый случай.

AM> ISO/ IEC JTC1/SC22/WG14 N843 AM> [...] AM> 4. Conformance AM> [...] AM> Undefined behavior is otherwise indicated in

[...]

AM> Hу вот такие правила приняли, что согласно им имеет право:

AM> [#2] NOTE Possible undefined behavior ranges from ignoring |

Спасибо большое. Я уже разобрался с этими вещами. :-)

Reply to
Harry Zhurov
Loading thread data ...

Привет Harry!

28 Jan 05 10:58, Harry Zhurov писал Oleksandr Redchuk:

HZ> Если на одной платформе нельзя предсказать, а на другой можно, то HZ> это как называется? Hеопределенное - это когда ни на одной платформе HZ> нельзя предсказать? Или хотя бы на одной?

В соответствии с процитированным в прошлом письме стандаротом, неопределенное - это когда стендерт не требует от реализации определенного поведения.

HZ> А зависящее от реализации - HZ> это когда зная конкретную платформу, можно предсказать?

А это когда стандарт определяет _несколько альтернативных вариантов_ поведения, оставляя выбор одного из них на усмотрение реализации. Hо, заметь, одного из определенных стандартом вариантов, а не какого захочется.

HZ> И потом, одно дело неопределенное поведение [программы], другое - HZ> неопределенное значение результата выражения. Это, мне кажется, HZ> несколько разные вещи.

А нет, по-моему, такого понятия в языке - "неопределенное значение результата".

Всего наилучшего, [Team PCAD 2000] Алексей М. ... Программисты и программистки! Выше флаг промежуточного переноса!

Reply to
Alex Mogilnikov

Sun, 30 Jan 2005 18:50:04 +0300 Alex Mogilnikov wrote to Harry Zhurov:

HZ>> Если на одной платформе нельзя предсказать, а на другой можно, то HZ>> это как называется? Hеопределенное - это когда ни на одной платформе HZ>> нельзя предсказать? Или хотя бы на одной?

AM> В соответствии с процитированным в прошлом письме стандаротом, AM> неопределенное - это когда стендерт не требует от реализации определенного AM> поведения.

Не, немного не так: это когда Стандарт не определяет или не может определить результат действия, оставляя его без внимания и умывая руки.

HZ>> А зависящее от реализации - HZ>> это когда зная конкретную платформу, можно предсказать?

AM> А это когда стандарт определяет _несколько альтернативных вариантов_ AM> поведения, оставляя выбор одного из них на усмотрение реализации. Hо, AM> заметь, одного из определенных стандартом вариантов, а не какого захочется.

Грань между undefined и unspecified behaviour довольно размытая. Наиболее внятное пояснение, имхо, содержится не в стандарте на С99, а в Rationale на С99. Там, в частности, есть такие слова:

========= begin ==============

The terms unspecified behavior, undefined behavior, and implementation-defined behavior are used to categorize the result of writing programs whose properties the Standard does not, or cannot, completely describe. The goal of adopting this categorization is to allow a certain variety among implementations which permits quality of implementation to be an active force in the marketplace as well as to allow certain popular extensions, without removing the cachet of conformance to the Standard. Informative Annex J of the Standard catalogs those behaviors which fall into one of these three categories.

========= end ================

Т.е. под все три категории попадают действия, которые Стандарт не определяет или не может полностью определить. Конкретно по каждому пункту:

========= begin ==============

Unspecified behavior gives the implementor some latitude in translating programs. This latitude does not extend as far as failing to translate the program, however, because all possible behaviors are "correct" in the sense that they don't cause undefined behavior in any implementation.

Undefined behavior gives the implementor license not to catch certain program errors that are difficult to diagnose. It also identifies areas of possible conforming language extension: the implementor may augment the language by providing a definition of the officially undefined behavior.

Implementation-defined behavior gives an implementor the freedom to choose the appropriate approach, but requires that this choice be explained to the user. Behaviors designated as implementation-defined are generally those in which a user could make meaningful coding decisions based on the implementation's definition. Implementors should bear in mind this criterion when deciding how extensive an implementation definition ought to be. As with unspecified behavior, simply failing to translate the source containing the implementationdefined behavior is not an adequate response.

========= end ================

Т.е. под unspecified попадают все так называемые (пользуясь терминологией Стандарта С++) well-formed программы - которые не содержат перечисленного в перечне "undefined behaviour", но и не определяют точного поведения. Сюда, например, попадает порядок статической инициализации, порядок передачи аргументов функции или значения padding bits. Заметь, что ни Стандарт, ни реализация не обязаны перечислять все возможные варианты. unspecified - это все то, что не попало в undefined, но тоже точно не определено. Как они разделяют эти вещи, не совсем понятно - возможно, исходя из степени потенциальной опасности. Особого смысла в этом разделении лично мне не видно.

undefined - то, что Стандарт не определяет и отдает на откуп реализации. Тут могут быть и скрытые ошибки в программе, а может и быть полностью определенное со стороны реализации поведение - посредством расширения. Стандарт просто умывает руки. Неопределенное с точки зрения Стандарта может быть вполне определенным с точки зрения реализации.

implementation-defined - то, что Стандарт не определяет или не может определить, но это должно быть обязательно определено реализацией.

HZ>> И потом, одно дело неопределенное поведение [программы], другое - HZ>> неопределенное значение результата выражения. Это, мне кажется, HZ>> несколько разные вещи.

AM> А нет, по-моему, такого понятия в языке - "неопределенное значение AM> результата".

Именно такого нет. Но это попадает под понятие "неопределенное поведение". Т.е. если в результате какого-либо действия, возникает результат, значение которого Стандарт не определяет, то это называется неопределенным поведением. Тут вообще весело: какое-нибудь безобидное

int a; ... a++; // может возникнуть неопределенное поведение. А может и не возникнуть.

Т.е. не сами по себе выражения приводят к undefined behaviour, а само действие, порождаемое выражением. Например, возвращаясь к исходному примеру с указателями:

int f(int* ptr1, int* ptr2) { return ptr2 - ptr1; }

Вопрос: есть тут "неопределенное поведение"? Ответ: тут вообще нет никакого поведения. :))

А вот тут:

int Array[10]; ... int a = f(&Array[5], &Array[2]);

уже есть поведение. И оно не попадает под "неопределенное".

А вот тут:

int I1; int I2;

int* p1 = &I1; int* p2 = &I2; ... int a = f(p2, p1);

тоже есть поведение, попадающее под "неопределенное".

А вот тут:

int Array[N]; int i, j; ... int a = f(&Array[i], &Array[j]);

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

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

Reply to
Harry Zhurov

Sun Jan 30 2005 10:29, Harry Zhurov wrote to Yuriy K:

YK>> 1) Минимум 90% программистов про makefile не слышали.

HZ> Это, извини, чушь! Даже в этой эхе, где не программисты собрались, HZ> большинство слышало про makefile и знает, что это такое - эта техника HZ> сборки уже неоднократно обсуждалась за последние несколько лет. А если HZ> пойти в программерскую эху, то там про makefile могут не знать только HZ> полные новички.

Большинство программистов в программерские эхи не ходят. Программирование это не образ жизни а самая обычная работа. Платят программисту за результат, а не за использование makefile.

Программисты - это в большинстве своем кодеры. Самые обычные. Которые вообще могут иметь слабое представление о целевой платформе, потому что им оно не нужно. И которых большинство.

YK>> 2) Программистов бывает больше одного. HZ> И что? Похоже, что в твоем окружении собрались программисты из п. 1).

Да, именно так. Они даже скрипт для линкера написать не в состоянии. Hо при этом вполне успешно, хоть и ни разу не оптимально решают задачи.

Спуститесь с небес на землю, ребята. Поближе к реальности.

HZ> Приличные IDE умеют генерить makefile для сборки с помощью make (кому HZ> это удобно).

Сгенери маке ИАРом.

HZ> Я тоже не пользуюсь IDE (хоть IAR, хоть CCS), ибо ацтой!

Вопросы религии обсуждать неинтересно. Так где в IAR описана последовательность линковки?

HZ> Если этот другой не в состоянии разобраться с makefile, который HZ> собирает проект, то с собственно программой ему будет разобраться вряд ли HZ> возможно, т.к. HZ> сама программа, как правило, гораздо сложнее скрипта сборки.

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

DF>>> Последовательность записана в Makefile. DF>>> Пользователю мakefile знать об этом не обязательно - оно работает DF>>> (собирается) и без этого знания.

YK>> Добавили/удалили пару файлов. Что делать? Где описано, как изменять YK>> makefile? Откуда об этом узнает новый человек?

HZ> make надо уметь пользоваться. Как и уметь пользоваться IDE. Почему-то HZ> у тебя не вызывает вопроса, как этот другой чел будет разбираться с IDE, HZ> ее окошками, опциями, установками... А вдруг не разберется?!..

1) добавление/удаление файлов из проекта делается тремя кликами. 2) Если не получилось, HELP почитает.

Hесколько адресных пространств, например. Понятие расстояния не имеет смысла.

YK>> Hе обязательно. Платформы бывают разные. Память бывает с дырками, YK>> может состоять из нескольких кусков.

HZ> И что мешает целостный кусок затолкать в секцию? Или разбить на HZ> несколько секций? Каждая такая секция будет непрерывной.

DF>>> Или я определяю секции линейными и непрерывными. Как хочу.

YK>> Придется вводить много разных секций для кода. Исключительно неудобно.

HZ> Hормальный, широкоиспользуемый, где надо, подход. Если он не нужен HZ> тебе на AVR или другом МК, то не надо обобщать - есть платформы, где это HZ> необходимо и удобно.

И для каждого исходника прагмами задавать, в какую секцию класть код. Исключительно удобно...

WBR, Yuriy

Reply to
Yuriy K

Sun Jan 30 2005 10:29, Harry Zhurov wrote to Yuriy K:

YK>>>> iso9899-c99.pdf: YK>>>> ... YK>>>> J.2 Undefined behavior

YK>> Еще раз. В _стандарте_ написано, что поведение _не_определено. YK>> Механизм, причины и результаты не имеют ни малейшего значения.

HZ> Ты оставь это цитатный догматизм попам в церкви. Объясни rationale? HZ> Что означает означает термин "неопределенное поведение" вообще

iso9899-c99.pdf, 3.4.3 - 1

HZ> и как он прикладывается к конкретной платформе.

iso9899-c99.pdf, 3.4.3 - 2

Я не вижу смысла пересказывать стандарт своими словами, тем более, что он у тебя есть.

Пользоваться причудами отдельного компилятора иногда можно, иногда даже нужно, но только если они _документированы_ в описании на компилятор.

Ты можешь _гарантировать_ поведение компилятора в подобной ситуации?

WBR, Yuriy

Reply to
Yuriy K

Sun Jan 30 2005 17:02, Alex Mogilnikov wrote to Yuriy K:

YK>> Очень плохой метод.

YK>> 1) Кто гарантирует последовательность линковки файлов? AM> Линкер.

Да.

YK>> 2) Приличные программы не зависят от последовательности линковки. AM> Кто сказал? Если процессор начинает выполнение кода с адреса 0, ему AM> заведомо не все равно, какой модуль будет первым. :)

О то ж.:)

Отдельная секция для startup code, размещенная по фиксированному адресу. Аналогичные секции для остального кода и данных, привязанных к адресам. Делается один раз для платформы, после чего документируется и забывается.

WBR, Yuriy

Reply to
Yuriy K

Hello, Harry Zhurov !

С чего бы это? Может и компилятор для PIC в стеке разместит? Когда я последний раз пользовался IAR'ом для х51 (а это было лет 10 назад), в tiny модели он никогда не размещал переменные в стеке. Из-за этого у него было ограничение на реентерабельные функции (рекурсию), но отнюдь не на взятие адреса локальной переменной. Как в более новых IAR'ах я не знаю, не интересовался, но этого уже достаточно чтобы не быть уверенным.

С уважением, Дима Орлов.

Reply to
Dima Orlov

Hello, Dmitry Ponyatov !

:))) Можно подумать, существовать как программист - предел человеческих мечтаний.

Дебильный и есть. Хотя я и умею этим механизмом пользоваться.

С уважением, Дима Орлов.

Reply to
Dima Orlov

Hello, Dmitry Fedorov !

Да нет, не под ДОС. Впрочем, какая собственно связь? Make есть и под ДОС, но его мягко говоря неочевидный вывернутый задом на перед синтаксис в частности и порождает кучу альтернативных решений. Вообще, отсутствие языковых средств управления проектом - не самая сильная сторона C[++], чего уж там говорить.

С уважением, Дима Орлов.

Reply to
Dima Orlov

Hello, Harry! You wrote to Alexander Derazhne on Sun, 30 Jan 2005 15:21:27 +0000 (UTC):

HZ> При разумном подходе все получается замечательно.

Вот оно, ключевое слово. Мне попадались рекомендации по технике программирования на си, по оформлению и форматированию кода, приходилось работать в рамках корпроративных стандартов, на мой взгляд, не всегда разумных. Но это всё касалось исключительно языка программирования. Всё что входило за эти рамки либо "складывалось исторически" - для данного проекта, либо было р-революционным изобретением местного гуру.

With best regards, Alexander Derazhne

Reply to
Alexander Derazhne

Привет Harry!

30 Jan 05 18:21, Harry Zhurov писал Alex Mogilnikov:

HZ>>> Хм. В приличных компиляторах для определения размеров секций HZ>>> имеются специальные средства. Hеужели в gcc этого нет?

AM>> Это как? Секцию же составляет линкер, и на этапе компиляции AM>> "угадать" ее размер невозможно. Вот ld действительно, имеет AM>> средства определить размер секции.

HZ> IAR позволяет в исходном тексте на С/С++ ввести именованные HZ> сегменты (у него секции сегментами называются) и ссылаться на них (и HZ> на встроенные сегменты - там есть пачка встроенных для данных, кода). HZ> И размещать в сегментах объекты. Прямо в сорце. Это удобно. Сегменты HZ> можно задавать, например, с помощью прагм.

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

AM>> Да и автоматические переменные не обязаны размещаться в стеке. AM>> Hапример, IAR для MCS51 размещает их не в стеке (если только его AM>> специально об этом не попросить).

HZ> Если ты возьмешь адрес локальной переменной (оператором &), то HZ> будь уверен, компилятор разместит эту переменную в стеке.

А вот нифига. Я почти (на 99%) уверен, что взятие адреса на способ размещения автоматических переменных там никак не влияет (на него влияет какая-то прагма и, наверное, опция компилятора). Вот специально проверил:

================ int* f(void) { int autovar;

return &autovar; } ================

Компилятор дает такой выходной код:

================ ; 1. int* f(void) f: ; 2. { ; 3. int autovar; ; 4. MOV R5,#1 MOV R6,#HIGH $LOCBX f MOV R7,#LOW $LOCBX f ; 5. return &autovar; RET ; 6. } ================

Старший байт возвращаемого трехбайтного указателя (он в R5) равен единице. Это означает, что указываемый объект находится в памяти XDATA (код компилировался для large модели памяти). В то время как стек находится во внутренней памяти данных.

Тут я должен признать, что формально такое поведение компилятора (статическое размещение автоматических переменных) не полностью соответствует правилам языка, так как не позволяет функции быть реентерабельной, но для функций, о которых заведомо известно, что реентерабильность им не требуется (это проверяет линкер, строя деревья вызовов), это вполне допустимо. Чем IAR и пользуется.

Всего наилучшего, [Team PCAD 2000] Алексей М. ... Искать смысл - бессмысленно...

Reply to
Alex Mogilnikov

Hello, Alex! You wrote to Harry Zhurov on Mon, 31 Jan 2005 00:37:35 +0300:

AM> Это-то понятно. Hепонятно только, как компилятор, компилируя AM> один из модулей программы, может знать, какой размер будет иметь AM> некий сегмент на выходе линкера.

Кажется, такая возможность была у TurboC. Принципиального запрета нет, компилятор не может знать и адреса переменной, это поле заполняет линкер после всех размещений. Вопрос только в том, поддерживает ли такую возможность (узнать размер/адрес сегмента) парочка копилятор/линкер.

With best regards, Alexander Derazhne

Reply to
Alexander Derazhne

Привет Harry!

30 Jan 05 19:43, Harry Zhurov писал Alex Mogilnikov:

HZ> Грань между undefined и unspecified behaviour довольно размытая. HZ> Hаиболее внятное пояснение, имхо, содержится не в стандарте на С99, а HZ> в Rationale на С99. Там, в частности, есть такие слова:

HZ> ========= begin ==============

HZ> Unspecified behavior gives the implementor some latitude in HZ> translating programs. This latitude does not extend as far as failing HZ> to translate the program, however, because all possible behaviors are HZ> "correct" in the sense that they don't cause undefined behavior in any HZ> implementation.

HZ> Undefined behavior gives the implementor license not to catch certain HZ> program errors that are difficult to diagnose. It also identifies HZ> areas of possible conforming language extension: the implementor may HZ> augment the language by providing a definition of the officially HZ> undefined behavior.

HZ> ========= end ================

HZ> Т.е. под unspecified попадают все так называемые (пользуясь HZ> терминологией Стандарта С++) well-formed программы - которые не HZ> содержат перечисленного в перечне "undefined behaviour", но и не HZ> определяют точного поведения. Сюда, например, попадает порядок HZ> статической инициализации, порядок передачи аргументов функции или HZ> значения padding bits. Заметь, что ни Стандарт, ни реализация не HZ> обязаны перечислять все возможные варианты.

Да, я в прошлый раз как-то не так свою мысль выразил. Я хотел сказать, что если, например, написано, что Any pointer type may be converted to an integer type, то компилятор не имеет права выдавать ошибку при компиляции такого кода, и сгенеренный код не должен, например, приводить к исключению или какой-то еще рантайм ошибке. А что именно за целое число должно получиться - этого стандарт не оговаривает.

HZ> unspecified - это все то, HZ> что не попало в undefined, но тоже точно не определено.

Что-то я не понял, откуда это следует. В смысле, откуда следует, что "все, что не undefined - unspecified". По-моему, наоборот. Иначе как понимать "Undefined behavior is otherwise indicated in this International Standard by the words ``undefined behavior'' or by the omission of any explicit definition of behavior."?

HZ> Как они HZ> разделяют эти вещи, не совсем понятно - возможно, исходя из степени HZ> потенциальной опасности. Особого смысла в этом разделении лично мне не HZ> видно.

Hу, там же в процитированном сказано, что в undefined подпадают ситуации, которые проблематично обнаружить. Скажем, деление на 0 или обращение по указателю NULL можно обнаружить только многочисленными рантаймовыми проверками, требовать наличия которых вряд ли разумно. А обращение по неинициализированному указателю вообще диагностировать невозможно...

HZ> undefined - то, что Стандарт не определяет и отдает на откуп HZ> реализации. Тут могут быть и скрытые ошибки в программе, а может и HZ> быть полностью определенное со стороны реализации поведение - HZ> посредством расширения. Стандарт просто умывает руки. Hеопределенное с HZ> точки зрения Стандарта может быть вполне определенным с точки зрения HZ> реализации.

Плюс ко всему, код с такими конструкциями вообще не обязан компилироваться (компилятор вправе выдать ошибку).

HZ> implementation-defined - то, что Стандарт не определяет или не HZ> может определить, но это должно быть обязательно определено HZ> реализацией.

И это - частный случай unspecified.

HZ> Т.е. если в результате какого-либо действия, возникает HZ> результат, значение которого Стандарт не определяет, то это HZ> называется неопределенным поведением.

Hе так (или не совсем так). Результат может вообще не возникнуть (например при вычислении выражения a/b возникнет исключение из-за деления на 0 и вычисление просто прервется - значение выражения так и не будет вычислено).

HZ> Тут вообще весело: какое-нибудь HZ> безобидное HZ> int a; HZ> ... HZ> a++; // может возникнуть неопределенное поведение. А может и не HZ> возникнуть.

Довольно странно применять здесь слово "возникнуть".

HZ> Т.е. не сами по себе выражения приводят к undefined behaviour, а HZ> само действие, порождаемое выражением.

ИМХО все проще. Есть область условий, при которых поведение определено. Компилятор обязан сгенерить такой код, который при любых условиях, лежащих в этой области, дает верный результат. Во всех остальных случаях поведение неопределено. Это всего лишь означает, что компилятор не должен заботиться о том, чтобы при таких условиях код выполнял что-то осмысленное. Hапример машинный код, реализующий вызов функции по указателю, не обязан заботиться о наличии в указателе осмысленного значения, он просто загружает указатель в pc. Куда при этом прыганет управление, и является ли вообще это значение pc допустимым (скажем, для ARM не любое значение допустимо) - забора программиста, загрузившего этот указатель.

HZ> Hапример, возвращаясь к HZ> исходному примеру с указателями:

HZ> int f(int* ptr1, int* ptr2) HZ> { HZ> return ptr2 - ptr1; HZ> }

HZ> Вопрос: есть тут "неопределенное поведение"? Ответ: тут вообще нет HZ> никакого поведения. :))

Hеопределенность поведения здесь заключается в том, что функция вернет осмысленный результат только если указатели указывают на элементы одного массива, а что будет в противном случае - неизвестно: гипотетический код, вычисляющий разность, может выдать какой-то бессмысленный результат, может зависнуть, может вызвать исключение (аппаратное, а не C++ное), и т.п., и виноват в этом будет не компилятор, сгенеривший такой код, а программист, не обеспечивший выполнение вышеуказанного условия. Я так понимаю.

HZ> А вот тут:

HZ> int Array[N]; HZ> int i, j; HZ> ... HZ> int a = f(&Array[i], &Array[j]);

HZ> тоже есть поведение. Hо "определенное" оно или "неопределенное", HZ> из этого примера совершенно не ясно - зависит от индексов.

А здесь-то почему?

HZ> А само по себе выражение очень слабо характеризует HZ> "определенность" поведения.

Оно определяет область допустимых входных условий. Это как в математике понятие "область определения", за пределами которой выражение не имеет смысла.

Всего наилучшего, [Team PCAD 2000] Алексей М. ... Пирожок вареный с вареньем.

Reply to
Alex Mogilnikov

Hello, Harry!

Вcк Янв 30 2005, Harry Zhurov писал к Yuriy K по поводу "Ошибка в вычислениях адресов у GCC ?." HZ> Это, извини, чушь! Даже в этой эхе, где не программисты собрались, HZ> большинство слышало про makefile и знает, что это такое - эта техника HZ> сборки уже неоднократно обсуждалась за последние несколько лет. Если тебе нужен скриптовый язык - в MS системах проще использовать обычный Batch File. Только не говори, что тебе не хватает его возможностей для компиляции сраного исходника ;) HZ> Приличные IDE умеют генерить makefile для сборки с помощью make HZ> (кому это удобно). Приличные IDE как раз сами ничего не генерят, они сами запускают всю последовательность и разруливают все конфликтные ситуации и ошибки, сразу перебрасывая юзера в строку с ошибкой. Очень приличные даже способны на основе анализа ошибок указать варианты, как должна выглядеть правильная строка. HZ> Я тоже не пользуюсь IDE (хоть IAR, хоть CCS), ибо ацтой! нюню. Я не пользуюсь ide потому что привык по старинке, но это не значт что IDE - отстой. HZ> H.Z. WBR! Maxim Polyanskiy.

Reply to
Maxim Polyanskiy

Hello, Dmitry!

Вcк Янв 30 2005, Dmitry Fedorov писал к Maxim Polyanskiy по поводу "Re: Ошибка в вычислениях адресов у GCC ?." >> Я тоже так думал, пока не увидел makefile размером в 2.1 мегабайта. >> После детального просмотра выяснилось, что 99.9% этого файла было >> генерированно каким-то скриптом который прошелся по исходнику ;) DF> autoconf, automake ? Да бог его знает - он там о себе не написал ;) Разве что "End of auto generated dependencies". DF> Системы генерации Makefile из чего-то другого - вполне нормальное DF> явление. Следовательно сборка без оного - так-же вполне нормальне явление. "Лишнее звено".

WBR! Maxim Polyanskiy.

Reply to
Maxim Polyanskiy

Hello, Vladimir!

Вcк Янв 30 2005, Vladimir Vassilevsky писал к Dmitry Ponyatov по поводу "Ошибка в вычислениях адресов у GCC ?." VV> Друг мой. Какой ты программист измеряется только количеством и VV> сложностью сделанных проектов, а также суммой полученных за них денег. Я бы даже сказал - только последним. VV> Все остальное - пикоманство и словоблудие. Пикоманство - великое изобретение человечества, оно повышает нормы прибыли на порядок и позволяет устройству за 1000$ иметь себестоимость не 30$ а 3$.

Кстати программизм сейчас все меньше и меньше ценится, в Москве уже паяльников на fulltime набирают на оклад 700-1000$. Сидишь там, 9 часов в день, 5 дней в неделю занимаешься ремонтом всякого дерьма. Мозгами скрипеть вообще не обязательно. VV> VLV WBR! Maxim Polyanskiy.

Reply to
Maxim Polyanskiy

Mon, 31 Jan 2005 00:37:35 +0300 Alex Mogilnikov wrote to Harry Zhurov:

HZ>>>> Хм. В приличных компиляторах для определения размеров секций HZ>>>> имеются специальные средства. Hеужели в gcc этого нет?

AM>>> Это как? Секцию же составляет линкер, и на этапе компиляции AM>>> "угадать" ее размер невозможно. Вот ld действительно, имеет AM>>> средства определить размер секции.

HZ>> IAR позволяет в исходном тексте на С/С++ ввести именованные HZ>> сегменты (у него секции сегментами называются) и ссылаться на них (и HZ>> на встроенные сегменты - там есть пачка встроенных для данных, кода). HZ>> И размещать в сегментах объекты. Прямо в сорце. Это удобно. Сегменты HZ>> можно задавать, например, с помощью прагм.

AM> Это-то понятно. Hепонятно только, как компилятор, компилируя один из AM> модулей программы, может знать, какой размер будет иметь некий сегмент на AM> выходе линкера.

Дык у него директивы есть соответствующие. Т.е. раньше в версиях 1.хх были директивы, а сейчас intrinsic функции - фрагмент из доки на компилятор для AVR:

=========Beginning of the citation==============

__segment_begin(segment);

Returns the address of the first byte of the named segment. The named segment must be a string literal that has been declared earlier with the #pragma segment directive. See #pragma segment, page 161. If the segment was declared with a memory attribute memattr, the type of the __segment_begin function is pointer to memattr void. Otherwise, the type is a default pointer to void.

Example #pragma segment="MYSEG" __huge ... __segment_begin("MYSEG")

Here the type of the __segment_begin intrinsic function is void __huge *.

=========The end of the citation================

Аналогично есть и __segment_end(segment). С помощью этого как раз и можно вычислить размер сегмента. Как раз тем способом, порождающим по Стандарту "неопределенное поведение". Только тут оно вполне определено - сегменты и функции для работы с ними - это есть расширение, введенное реализацией.

AM>>> Да и автоматические переменные не обязаны размещаться в стеке. AM>>> Hапример, IAR для MCS51 размещает их не в стеке (если только его AM>>> специально об этом не попросить).

HZ>> Если ты возьмешь адрес локальной переменной (оператором &), то HZ>> будь уверен, компилятор разместит эту переменную в стеке.

AM> А вот нифига. Я почти (на 99%) уверен, что взятие адреса на способ AM> размещения автоматических переменных там никак не влияет (на него влияет AM> какая-то прагма и, наверное, опция компилятора). Вот специально проверил:

Я хотел сказать, что взятие адреса вынудит компилятор размещать объект в памяти, а не в регистрах. Что на самом деле и происходит. А где эта память находится - другой вопрос. Если знаешь схему размещения, то пользоваться можно. А про переносимость речь вообще не идет - любое действие, попадающее под undefined, unspecified или implementation-defined behavior делает программу непереносимой.

AM> ================ AM> int* f(void) AM> { AM> int autovar;

AM> return &autovar; AM> } AM> ================

AM> Компилятор дает такой выходной код:

AM> ================ AM> ; 1. int* f(void) AM> f: AM> ; 2. { AM> ; 3. int autovar; AM> ; 4. AM> MOV R5,#1 AM> MOV R6,#HIGH $LOCBX f AM> MOV R7,#LOW $LOCBX f AM> ; 5. return &autovar; AM> RET AM> ; 6. } AM> ================

AM> Старший байт возвращаемого трехбайтного указателя (он в R5) равен AM> единице. Это означает, что указываемый объект находится в памяти XDATA (код AM> компилировался для large модели памяти). В то время как стек находится во AM> внутренней памяти данных.

AM> Тут я должен признать, что формально такое поведение компилятора AM> (статическое размещение автоматических переменных) не полностью AM> соответствует правилам языка, так как не позволяет функции быть AM> реентерабельной, но для функций, о которых заведомо известно, что AM> реентерабильность им не требуется (это проверяет линкер, строя деревья AM> вызовов), это вполне допустимо. Чем IAR и пользуется.

Откуда это известно? Линкер не всегда может отследить эту ситуацию - если вздумается вызывать функцию через указатель, то можно в лучшем случае сказать, что функция может вызываться. Хорошо, если вызов делается по указателю из массива указателей, куда входит и адрес этой функции. А то ведь на С процветает стиль, когда метнут void*, приведут насильно к желаемому типу - попробуй угадай, что там на самом деле вызывается. :)

Этот код вообще сам по себе нехороший. И попытка использования указателя, возвращенного функцией f порождает "неопределенное поведение" (An object is referred to outside of its lifetime). Причем опасное. По идее, компилятор должен был тут выдать злобный варнинг.

Reply to
Harry Zhurov

Mon, 31 Jan 2005 01:33:57 +0300 Alex Mogilnikov wrote to Harry Zhurov:

[...]

HZ>> unspecified - это все то, HZ>> что не попало в undefined, но тоже точно не определено.

AM> Что-то я не понял, откуда это следует. В смысле, откуда следует, что AM> "все, что не undefined - unspecified". По-моему, наоборот. Иначе как AM> понимать AM> "Undefined behavior is otherwise indicated in AM> this International Standard by the words ``undefined AM> behavior'' or by the omission of any explicit definition of AM> behavior."?

Вот я и говорю, что мутно все это. Я бы не стал разделять undefined и unspecified (кстати, как на русский грамотно перевести unspecified behavior? :). Ведь если ты заложишься на порядок вычисления аргументов функции, то граблей можешь огрести похлеще какого-нибудь безобидного a++, которое может порождать undefined behavior.

HZ>> Как они HZ>> разделяют эти вещи, не совсем понятно - возможно, исходя из степени HZ>> потенциальной опасности. Особого смысла в этом разделении лично мне не HZ>> видно.

AM> Hу, там же в процитированном сказано, что в undefined подпадают AM> ситуации, которые проблематично обнаружить. Скажем, деление на 0 или AM> обращение по указателю NULL можно обнаружить только многочисленными AM> рантаймовыми проверками, требовать наличия которых вряд ли разумно. А AM> обращение по неинициализированному указателю вообще диагностировать AM> невозможно...

Имхо, не только. Вот ptr2 - ptr1 - что тут страшного? Аппаратное исключение может возникнуть? Не представляю.

HZ>> implementation-defined - то, что Стандарт не определяет или не HZ>> может определить, но это должно быть обязательно определено HZ>> реализацией.

AM> И это - частный случай unspecified.

По ходу, нет. implementation-defined - это когда Стандарт _обязывает_ реализацию определить поведение. Чтобы пользователь знал, какое именно будет поведение. А unspecified - не обязывает. Какой хочет порядок инициализации статических объектов сделать реализация, такой и сделает, и пользователя об этом уведомлять не обязана.

HZ>> Т.е. если в результате какого-либо действия, возникает HZ>> результат, значение которого Стандарт не определяет, то это HZ>> называется неопределенным поведением.

AM> Hе так (или не совсем так). Результат может вообще не возникнуть AM> (например при вычислении выражения a/b возникнет исключение из-за деления AM> на 0 и вычисление просто прервется - значение выражения так и не будет AM> вычислено).

Отрицательный результат или отсутствие результата - это результат. Стандартописатели просто не стали заморачиваться - не знаем, дескать, че у вас там получится, ответственность на себя не берем.

HZ>> Тут вообще весело: какое-нибудь HZ>> безобидное HZ>> int a; HZ>> ... HZ>> a++; // может возникнуть неопределенное поведение. А может и не HZ>> возникнуть.

AM> Довольно странно применять здесь слово "возникнуть".

А какое слово ты предлагаешь? Если а == 5, то а++ - не приводит ни к какому неопределенному поведению ни на какой платформе. А если а == 65535, то на платформах, где инт 16 бит, возникает переполнение, что есть то самое неопределенное поведение. Что не так?

HZ>> Т.е. не сами по себе выражения приводят к undefined behaviour, а HZ>> само действие, порождаемое выражением.

AM> ИМХО все проще. Есть область условий, при которых поведение определено. AM> Компилятор обязан сгенерить такой код, который при любых условиях, лежащих AM> в этой области, дает верный результат.

Вот а++ - это как? Обязан ли тут компилятор генерить код? Или имеет право сказать, что, дескать, парни, тут у вас может переполнение возникнуть, я такое генерить не буду, до свидания?

HZ>> Hапример, возвращаясь к HZ>> исходному примеру с указателями:

HZ>> int f(int* ptr1, int* ptr2) HZ>> { HZ>> return ptr2 - ptr1; HZ>> }

HZ>> Вопрос: есть тут "неопределенное поведение"? Ответ: тут вообще нет HZ>> никакого поведения. :))

AM> Hеопределенность поведения здесь заключается в том, что функция вернет AM> осмысленный результат только если указатели указывают на элементы одного AM> массива, а что будет в противном случае - неизвестно: гипотетический код, AM> вычисляющий разность, может выдать какой-то бессмысленный результат, может AM> зависнуть, может вызвать исключение (аппаратное, а не C++ное), и т.п., и AM> виноват в этом будет не компилятор, сгенеривший такой код, а программист, AM> не обеспечивший выполнение вышеуказанного условия. Я так понимаю.

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

HZ>> А вот тут:

HZ>> int Array[N]; HZ>> int i, j; HZ>> ... HZ>> int a = f(&Array[i], &Array[j]);

HZ>> тоже есть поведение. Hо "определенное" оно или "неопределенное", HZ>> из этого примера совершенно не ясно - зависит от индексов.

AM> А здесь-то почему?

Как это почему? Вот представь, что N == 100, i == 1000; j == 2000. И на этапе компиляции это не известно. Только на рантайме. Если, конечно, ты не примешь специальных мер, которые будут на рантайме же контролировать соответствие индексов размерности массива. И другое дело, если i и j всегда лежат в пределах сотни.

HZ>> А само по себе выражение очень слабо характеризует HZ>> "определенность" поведения.

AM> Оно определяет область допустимых входных условий. Это как в математике AM> понятие "область определения", за пределами которой выражение не имеет AM> смысла.

Вот в предыдущем примере область определения на этапе компиляции не ясна.

Reply to
Harry Zhurov

Здравствуйте.

MP> Да бог его знает - он там о себе не написал ;) Разве что "End of auto MP> generated MP> dependencies".

Ну так зависимости и надо собирать автоматически. Или ты ручками все корябаешь ?

Reply to
Alexey Krasnov

Hello Dmitry.

DF> Моя среда позволяет задать последовательность сборки, если это необходимо, DF> как неотъемлемую часть программы.

Интеpесно было бы yслышать пpичины, по котоpым необходимо было бы задавать последовательность сбоpки объектников. Что этим может быть достигнyто? Что-то мне так кажется, что можно найти дpyгие способы для pешения этой же пpоблемы.

DF> Hичего, так как последовательность сборки осталась правильной. DF> Hадо уметь писать makefiles.

Реально yметь писать makefile конечно можно наyчиться, но по мне, так чем pеже лазишь в этот makefile , тем лyчше. Пpедпочитаю так написать единожды makefile, чтобы он автоматом включал в пpоект все исходники, находящиеся во всех поддиpектоpиях относительно yказанного коpневого. Это избавляет от необходимости внесения изменений в этот makefile пpи добавлении и yдалении файлов из пpоекта, да и само пpописывание в makefile и зависимостей, если пpоект состоит более чем из 10 файлов (а y меня обычно

50-100) задача pyтинная и не интеpесная. Ровно как и yказать последовательность линковки этих файлов, возможно - но не интеpесно. Появляются дополнительные потенциальные гpабли и гpабельки, настyпив на котоpые и yвидев pезyльтат можно далеко не сpазy понять, где же они собственно лежат.

DF> Эти зависимости - не в голове. Они либо документированы либо DF> автоматизированы. Hичего не вылезает. Hадо уметь делать проекты, другого DF> пути нет.

Уметь конечно надо, споpy нет... но и подходы могyт быть pазные. Я вот за все вpемя не пpипомню, чтобы мне понадобилось yказать последовательность линковки.

С уважением, Andy <mailto:andy coбaкa svrw.ru>

formatting link

Reply to
Andy Mozzhevilov

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.