Croessmah, понимаешь, «разминка для мозгов» — это предложение подумать над неочевидными решениями вроде вполне очевидных задач. И чёткость решения здесь важна.
Вот, например, задача из этого топика показывает, что выполнение программы начинается не с функции main(), и ей не заканчивается. Следовательно при отладке реальных программ на эти моменты тоже надо обращать внимание. Что для начинающих может быть совершенно не очевидно.
А по теме топика можешь добавить что-то принципиально отличное от уже предложенного?
Интересно не знать, что делает эта ф-ция, а уметь написать её самому. Мне кажется, или без грязной ассемблерной вставки, в форме запихивания в стек адрес функций (иначе как объяснить порядок выполнения?), тут не обошлось?
Фтьiкай
Думаю, что написать ее самому проблематично. Надо знать адрес (точнее говоря, символическое имя этого адреса) стека функций завершения. Маловероятно, что для этих целей используется системный стек. Скорее всего, стек для функций завершения отдельный на 32 адреса, а в программе при завершении вызывается некая функция (часть си-шной RTS), которая проверяет наличие в этом стеке адресов функций и, если таковые имеются в цикле вызывает эти функции до исчерпания стека.
Такого быть не может. По вашему стандартая библиотека неявно становится частью «сырого» языка, но ведь это не так. (не будем вспоминать std::type_info, возвращаемый typeid)
По вашему стандартная библиотека неявно становится частью «сырого» языка
На счёт «сырого» я бы поспорил... А в остальном, оно так и есть.
На самом деле, так было задумано изначально. Поскольку С разрабатывался как высокоуровневый кроссплатформенный «ассемблер», то все платформозависимые вещи выносились в стандартную библиотеку, начиная от size_t и заканчивая printf().
Реализация стандартной библиотеки уникальна для каждого компилятора (и, естественно, платформы). И, скорее всего, написана она на 90% на ассемблере. (Конечно можноprintf реализовать на С, а вот «variadic functions» — уже явно ассемблер.)
Croessmah, то, что ты написал — действительно вариации на темы инициализации глобальной переменной и конструктора глобального объекта.
назначение стандартных обработчиков для разных ситуаций.
А вот это момент более интересный.
Пока были перечислены следующие способы выполнения кода вне функции main():
1. Конструктор глобального объекта.
2. Деструктор глобального объекта.
3. Инициализация глобальной переменной с помощью вызова подпрограммы.
4. Функция-обработчик нормального завершения программы (посредством вызова atexit()).
Печально, когда так или иначе приходится использовать стандартную библиотеку :(
На счёт «сырого» я бы поспорил...
А что не так в «сыром»?
Фтьiкай
Видимо как-то можно обойтись и без стандартной библиотеки. Пишут же операционки на С. Согласен, что в этом случае без ассемблера не обходится, но это, видимо, совсем низкий уровень: загрузчик, планировщик и т.п. А когда пишут ОС с нуля, там по началу даже системных вызовов нет. Т.е. исполняемые модули новой ОС должны быть написаны на чистом С (как альтернатива ассемблеру) без использования стандартной библиотеки с минимальными прологами и эпилогами. Причем прологи и эпилоги должны удовлетворять соглашениям новой ОС.
Наверное надо подвести итоги, поскольку новых идей больше не появляется.
Добавлю от себя пять копеек )) Ни кто не вспомнил про функцию-обработчик аварийного завершения программы при необработанном исключении (посредством вызова функции set_terminate()).
На эту же тему есть unexpected() и set_unexpected(). Объявлены deprecated. Перехват управления осуществляется только в случае, когда в функции происходит исключение, которое непродекларировано в списке исключений для этой функции. В MSVC++ 2013 не поддерживается.
Ещё есть инициализация статического члена класса. Причём создавать объект класса не обязательно. Но этот случай, по сути, очень близок к инициализации глобальной переменной. Поэтому отдельным пунктом не вынесен.
Итоговый текст программы:
#include <iostream>
#include <cstdlib>
#include <exception>
using namespace std;
// Инициализация глобальной переменной посредством вызова функции
int func() {
cout << "initialization of global variable by function call (1)" << endl;
return 0;
}
int a = func();
// Инициализация глобальной переменной посредством выражения с побочным эффектом
ostream *os = &(cout << "initialization of global variable by expression with side effect (2)" << endl);
// Конструктор и деструктор глобального объекта
class Class {
public:
Class();
~Class();
};
Class::Class() {
cout << "constructor of global object (3)" << endl;
}
Class::~Class() {
cout << "destructor of global object (4)" << endl;
}
Class b;
// Функция-обработчик завершения программы (посредством вызова atexit())
void atexit_func() {
cout << "program termination handler via atexit() (5)" << endl;
}
int u = atexit(atexit_func);
// Функции-обработчики аварийного завершения программы при необработанном исключении
// (посредством вызовов функций set_unexpected() и set_terminate())
void unexp() {
cout << "unhandled exception handler via set_unexpected() (7)" << endl;
terminate();
}
unexpected_handler uf = set_unexpected(unexp);
void term() {
cout << "unhandled exception handler via set_terminate() (6)" << endl;
exit(1);
}
terminate_handler tf = set_terminate(term);
int raise_except() throw(double) {
throw 1; // исключение int, продекларировано только double
return 0;
}
int t = raise_except();
// точка входа ))
int main()
{
return 0;
}
Хотя... возможно где-то что-то ещё есть на эту тему. Продолжение следует? ;-)
Croessmah, ты не уточнил что ты имеешь ввиду под "обработчиками для разных ситуаций". Один случай ты показал — atexit(). Перехват terminate() — совсем другая область и другой механизм работы. Если ты знаешь ещё какие-то обработчики — конкретизируй.
Один случай ты показал — atexit(). Перехват terminate() — совсем другая область и другой механизм работы.
Смысл я вкладывал не в конечный механизм, а в то, что мы используем переменную со статическим временем хранения(в данном случае глобальную) для того, чтобы вызвать код, который назначит обработчик и потом спровоцирует его вызов.
Если ты знаешь ещё какие-то обработчики — конкретизируй.
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
А как работает atexit?
atexit()
— принимается.Только я не понял зачем ты такую сложную программу написал. Вполне хватит и
Описалово функции atexit().
И это ещё не всё.
Wow! Какой хороший сайт! Надеюсь его не заблокируют за нарушение авторских прав!!!
ну так тема же:
вот пусть, разминают. Я на таких задачках уже наразминался вдоволь )))
Croessmah, понимаешь, «разминка для мозгов» — это предложение подумать над неочевидными решениями вроде вполне очевидных задач. И чёткость решения здесь важна.
Вот, например, задача из этого топика показывает, что выполнение программы начинается не с функции
main()
, и ей не заканчивается. Следовательно при отладке реальных программ на эти моменты тоже надо обращать внимание. Что для начинающих может быть совершенно не очевидно.А по теме топика можешь добавить что-то принципиально отличное от уже предложенного?
Интересно не знать, что делает эта ф-ция, а уметь написать её самому. Мне кажется, или без грязной ассемблерной вставки, в форме запихивания в стек адрес функций (иначе как объяснить порядок выполнения?), тут не обошлось?
Думаю, что написать ее самому проблематично. Надо знать адрес (точнее говоря, символическое имя этого адреса) стека функций завершения. Маловероятно, что для этих целей используется системный стек. Скорее всего, стек для функций завершения отдельный на 32 адреса, а в программе при завершении вызывается некая функция (часть си-шной RTS), которая проверяет наличие в этом стеке адресов функций и, если таковые имеются в цикле вызывает эти функции до исчерпания стека.
Принципиально отличное врядли возможно в соответствии со стандартом. Будут лишь вариации одного и того же, например, как это:
...
То бишь упремся в инициализацию статической переменной, в вызов функции и в назначение стандартных обработчиков для разных ситуаций.
Такого быть не может. По вашему стандартая библиотека неявно становится частью «сырого» языка, но ведь это не так. (не будем вспоминать
std::type_info
, возвращаемыйtypeid
)На счёт «сырого» я бы поспорил... А в остальном, оно так и есть.
На самом деле, так было задумано изначально. Поскольку С разрабатывался как высокоуровневый кроссплатформенный «ассемблер», то все платформозависимые вещи выносились в стандартную библиотеку, начиная от
size_t
и заканчиваяprintf()
.Реализация стандартной библиотеки уникальна для каждого компилятора (и, естественно, платформы). И, скорее всего, написана она на 90% на ассемблере. (Конечно можно
printf
реализовать на С, а вот «variadic functions» — уже явно ассемблер.)Croessmah, то, что ты написал — действительно вариации на темы инициализации глобальной переменной и конструктора глобального объекта.
А вот это момент более интересный.
Пока были перечислены следующие способы выполнения кода вне функции
main()
:1. Конструктор глобального объекта.
2. Деструктор глобального объекта.
3. Инициализация глобальной переменной с помощью вызова подпрограммы.
4. Функция-обработчик нормального завершения программы (посредством вызова
atexit()
).Печально, когда так или иначе приходится использовать стандартную библиотеку :(
А что не так в «сыром»?
Видимо как-то можно обойтись и без стандартной библиотеки. Пишут же операционки на С. Согласен, что в этом случае без ассемблера не обходится, но это, видимо, совсем низкий уровень: загрузчик, планировщик и т.п. А когда пишут ОС с нуля, там по началу даже системных вызовов нет. Т.е. исполняемые модули новой ОС должны быть написаны на чистом С (как альтернатива ассемблеру) без использования стандартной библиотеки с минимальными прологами и эпилогами. Причем прологи и эпилоги должны удовлетворять соглашениям новой ОС.
porshe, несколько странно называть «сырым» язык с почти 45-летней историей ))
А чего печального в использовании стандартной библиотеки?
Похоже, есть различие в понимании терминов. «Сырой» значит голый язык, то есть язык без библиотек, с одними только инстсрукциями и операторами.
Печально не использование стандартной библиотеки, а необходимость этого использования, даже если я не хочу этого.
Наверное надо подвести итоги, поскольку новых идей больше не появляется.
Добавлю от себя пять копеек )) Ни кто не вспомнил про функцию-обработчик аварийного завершения программы при необработанном исключении (посредством вызова функции
set_terminate()
).На эту же тему есть
unexpected()
иset_unexpected()
. Объявлены deprecated. Перехват управления осуществляется только в случае, когда в функции происходит исключение, которое непродекларировано в списке исключений для этой функции. В MSVC++ 2013 не поддерживается.Ещё есть инициализация статического члена класса. Причём создавать объект класса не обязательно. Но этот случай, по сути, очень близок к инициализации глобальной переменной. Поэтому отдельным пунктом не вынесен.
Итоговый текст программы:
Хотя... возможно где-то что-то ещё есть на эту тему. Продолжение следует? ;-)
Я ж писал:
)))
Croessmah, ты не уточнил что ты имеешь ввиду под "обработчиками для разных ситуаций". Один случай ты показал —
atexit()
. Перехватterminate()
— совсем другая область и другой механизм работы. Если ты знаешь ещё какие-то обработчики — конкретизируй.Смысл я вкладывал не в конечный механизм, а в то, что мы используем переменную со статическим временем хранения(в данном случае глобальную) для того, чтобы вызвать код, который назначит обработчик и потом спровоцирует его вызов.
Да тот же set_new_handler
А своя реализация
atexit
считается способом? :)Правда, тут опять же используется деструктор глобального объекта, но зато сам написал, без этих ваших библиотек!1!1 (шутка, конечно).