Форум программистов, компьютерный форум CyberForum.ru

Почему глобальный объект, объявленный до main, конструируется в ней? - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 17, средняя оценка - 4.82
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,271
13.09.2011, 17:16     Почему глобальный объект, объявленный до main, конструируется в ней? #1
Всё просто, имеем код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <windows.h>
#include <stdio.h>
using namespace std;
 
class x {
 public:
 x () { 
  printf ("konstruktor\n");     
 }
};
x x_;
 
int main() {
 printf ("%x\n", main);
 getchar();
 return 0;
}
запускаем, видим:
C++
1
2
konstruktor
401290
последнее значение адрес main, у каждого своё.
Так, открываем прогу в отладчике OllyDbg, стоим на точке входа (у всех своя, у меня) 401220, ставим бряк на 401290, на main, запускаем. Видим, что бряк сработал, но конструктор не вызвался! Чуть пониже в окне дизассембра видим вызов printf с аргументом "konstruktor", то есть объект конструируется в main. Как такое может быть? Компилятор g++. Спасибо, кто откликнется.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
13.09.2011, 17:16     Почему глобальный объект, объявленный до main, конструируется в ней?
Посмотрите здесь:

C++ _tmain(...) вместо main(...) Почему?
Почему ругается на не объявленный идентификатор C++
C++ Динамический глобальный объект.
Глобальный объект C++
C++ Почему не хочет возвращать main() ?
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,271
13.09.2011, 18:56  [ТС]     Почему глобальный объект, объявленный до main, конструируется в ней? #21
Цитата Сообщение от Evg Посмотреть сообщение
Ятебе уже говорил, что вставать надо не на начале функции main, а на первом операторе.
Я записал, На бумажку на всякий случай, чтобы не было недоразумений. Вот как вы сказали:
Цитата Сообщение от Evg Посмотреть сообщение
По честному брекпоинт надо ставить на первый оператор main'а
По моему первый оператор main и есть начало main. Не? Если не так, уточните понятия, как это сделал я.
За printf: как бы не хотелось мне думать, что я просто "не заметил" вызова конструктороа, это не так. Брякаюсь на начало main (первый оператор или как его там), пошаговто трассирую и попадаю на printf с параметром "konstruktor". Так что увы и ах, объект создаётся в maine, что ставит с ног на голову мои и без того шаткие знания. Ребята, может в опциях компилятора надо что поправить? Я щас это смотрю уже.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
13.09.2011, 19:18     Почему глобальный объект, объявленный до main, конструируется в ней? #22
Цитата Сообщение от kravam Посмотреть сообщение
По моему первый оператор main и есть начало main. Не?
Это не так. В начале процедуры main (с точки зрения бинарного кода и отладчика), как и в начале любой процедуры идёт так называемый пролог. Как правило это настройка стека, настройка указателей стека и прочих вещей, о которых пользователю знать незачем, потому как в языке программирования нет этих понятий (на то он и язык, чтобы не опускаться до уровня системы команд, программных соглашений и т.п.)

Цитата Сообщение от kravam Посмотреть сообщение
Если не так, уточните понятия, как это сделал я.
Какие именно понятия? Что такое "первый оператор main'а"? В твоём случае это printf (который внутри main'а).

Цитата Сообщение от kravam Посмотреть сообщение
Брякаюсь на начало main (первый оператор или как его там),
Ты уж определись, на начало main (низкоуровневое понятие) или на первый оператор main (высокоуровневое понятие)

Цитата Сообщение от kravam Посмотреть сообщение
пошаговто трассирую и попадаю на printf с параметром "konstruktor".
Внимательно читай пост #9. А ещё лучше, покажи дизассемблер своей программы

Добавлено через 1 минуту
Т.е. пост #4
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,271
13.09.2011, 19:35  [ТС]     Почему глобальный объект, объявленный до main, конструируется в ней? #23
Всё понятно.
Значит, ещё раз. Чтобы куда-то брякнуться, мне надо цифры какие-то. Адрес то есть. Я его получаю так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <windows.h>
#include <stdio.h>
using namespace std;
 
class x {
 public:
 x () { 
  printf ("привет\n");     
 }
};
x x_;
 
int main() {
 printf ("%x\n", main);
 getchar();
 return 0;
}
И вижу 401290 (это я первый пост дублирую)
Потом эту прогу открываю в отладчике и ставлю бряк на 401290, думая, что это начало main (естессно, ошибочно, ибо я всегда думаю ошибочно)

Ваш вариант нахождения адреса для бряка обрисуйте пожалуйста. (Только пожалуйста не "первый оператор", а то мы с вами так далеко не уйдём.)

Добавлено через 1 минуту
...Кстати, пост номер 9 на корню зарубает мою тягу к знаниям.
grizlik78
Эксперт С++
 Аватар для grizlik78
1884 / 1416 / 102
Регистрация: 29.05.2011
Сообщений: 2,961
13.09.2011, 19:43     Почему глобальный объект, объявленный до main, конструируется в ней? #24
Результаты экспериментов.

Откопал MinGW 3.4.5 и GDB 6.8
В этой связке у меня работает не так, как у ТС.
Если установить breakpoint на имя main или на адрес, который выводится потом в printf (это 2 разных места) то в любом случае к моменту остановки слово konstruktor уже выведено.
Если смотреть ассемблерный код, то внутри функции main можно увидеть вызов функции __main.
Функция __main проверяет, вызывалась ли она уже, и если нет, то вызывает конструкторы глобальных объектов (функция __do_global_ctors).
Так что формально, вызов конструкторов возможен уже после входа в main.
Но.
Первый раз функция __main вызывается на этапе инициализации из функции __mingw_CRTStartup, ещё до вызова функции main
Так что в моей версии конструкторы вызываются до main, как и должно быть.

Но программисту все эти тонкости всё-равно ни к чему, если всё работает как надо.

Кстати, MinGW 4.5.2 строит код точно так же, а вот в Linux/x86_64 GCC 4.5.1 никаких посторонних вызовов внутри функции main не обнаружено.
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,271
13.09.2011, 19:58  [ТС]     Почему глобальный объект, объявленный до main, конструируется в ней? #25
У меня 3.4.2, можешь скинуть куда-нибудь?

Добавлено через 3 минуты
Хотя...
Цитата Сообщение от grizlik78 Посмотреть сообщение
Если установить breakpoint на имя main или на адрес, который выводится потом в printf (это 2 разных места)
как понять брейкпонт на имя? Тебе отладчик подсказывает, где находится имя?
grizlik78
Эксперт С++
 Аватар для grizlik78
1884 / 1416 / 102
Регистрация: 29.05.2011
Сообщений: 2,961
13.09.2011, 20:00     Почему глобальный объект, объявленный до main, конструируется в ней? #26
Кого скинут? 3.4.5, на котором я экспериментировал, входит в стостав wxDev-C++.
Версия 4.5.2 с sourceforge, кажется. У niXman в подписи можно найти 4.6.1 с бустом.

Цитата Сообщение от kravam Посмотреть сообщение
как понять брейкпонт на имя? Тебе отладчик подсказывает, где находится имя?
GDB без оболочек является консольным отладчиком. Командами управляется.
То есть я имел в виду команду
Код
break main
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,271
13.09.2011, 20:20  [ТС]     Почему глобальный объект, объявленный до main, конструируется в ней? #27
А с этим как быть?
Цитата Сообщение от grizlik78 Посмотреть сообщение
Отладчик не обязательно отражает истинное положение вещей. В ассемблерный код посмотреть надёжнее всего. Пожалуй, пойду посмотрю.
============================================
А точнее если: что же тогда выводится по
C++
1
 printf ("%x\n", main);
если ты ставишь бряк на другой адрес?

Добавлено через 11 минут
Час от часу не легче, оказывается, на main нельзя брать адрес:
http://msdn.microsoft.com/ru-ru/library/a59adt5z.aspx
Cannot have its address taken.

Но тогда это кое-что проясняет (хотя g++ отношение к мелкомягким не имеет, но тем не менее), а именно: адрес main, полученный программным путём- фуфловый. Значит, может быть действительно, брякаться надо на
C++
1
printf ("%x\n", main);
но не потому, что это правильно, а потому, что больше некуда. Программно-то адрес main не получить!
grizlik78
Эксперт С++
 Аватар для grizlik78
1884 / 1416 / 102
Регистрация: 29.05.2011
Сообщений: 2,961
13.09.2011, 20:40     Почему глобальный объект, объявленный до main, конструируется в ней? #28
Цитата Сообщение от kravam Посмотреть сообщение
Час от часу не легче, оказывается, на main нельзя брать адрес
Да, об этом я уже сказал, но это не выдумка Microsoft, это описано в стандарте C++. Впрочем, к GCC это не относится, он разрешает получение адреса и рекурсивный вызов main().

Цитата Сообщение от kravam Посмотреть сообщение
адрес main, полученный программным путём- фуфловый.
В случае VisualStudio компилятор просто не позволит получить адрес. В случае GCC адрес самый что ни на есть настоящий — начало кода функции main().

Цитата Сообщение от kravam Посмотреть сообщение
если ты ставишь бряк на другой адрес?
Как я уже говорил, я ставил breakpoint и по символическому имени, и по машинному адресу начала main. В обоих случаях конструктор уже был вызван.

Добавлено через 5 минут
Собственно, разница между этими точками останова только в том, что по символическому имени пропускается настройка стека.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
13.09.2011, 21:01     Почему глобальный объект, объявленный до main, конструируется в ней? #29
Цитата Сообщение от kravam Посмотреть сообщение
Значит, ещё раз. Чтобы куда-то брякнуться, мне надо цифры какие-то
Единственный человеческий способ остановиться в правильной точке - это скомпилировать с отладочной информацией и установить брекпоинт в виде "break main" или "break t.c:14" (я так понимаю, что у тебя отладчик gdb). Другого не дано.

Нечеловеческий способ - это продизассемблировать программу и глазками посмотреть адрес, который будет соответствовать первому оператору (если понимаешь в ассемблере). В режиме с оптимизациями ты этого в принципе уже получить точным образом, потому как компилятор скорее всего коды перемешает и не факт, что в программе найдётся точная граница между тем, как "первый оператор уже выполнен" и "первый оператор ещё не выполнен", потому что это на языке только один оператор, а в коде это будет несколько машинных инструкций, которые могут перемешаться с инструкциями другого оператора
grizlik78
Эксперт С++
 Аватар для grizlik78
1884 / 1416 / 102
Регистрация: 29.05.2011
Сообщений: 2,961
13.09.2011, 21:13     Почему глобальный объект, объявленный до main, конструируется в ней? #30
Цитата Сообщение от Evg Посмотреть сообщение
я так понимаю, что у тебя отладчик gdb
Так вроде ж OllyDbg у него. Фиг его знает, как там с интеграцией с кодом от MinGW. Это я GDB мучил.
ValeryLaptev
Эксперт C++
1005 / 784 / 46
Регистрация: 30.04.2011
Сообщений: 1,595
13.09.2011, 21:27     Почему глобальный объект, объявленный до main, конструируется в ней? #31
Цитата Сообщение от grizlik78 Посмотреть сообщение
А какая, по большому счёту разница, если стандарт С++ не разрешает получение адреса функции main()? Правда как-раз именно GCC это ограничение не выполняет и разрешает рекурсивный вызов функции main() в C++.
Раньше, помнится, и борландовские компиляторы разрешали. Баловался сам. Удобно параметры командной строки обрабатывать без цикла - рекурсивным вызовом.
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,271
13.09.2011, 21:29  [ТС]     Почему глобальный объект, объявленный до main, конструируется в ней? #32
Цитата Сообщение от Evg Посмотреть сообщение
Нечеловеческий способ - это продизассемблировать программу и глазками посмотреть адрес, который будет соответствовать первому оператору (если понимаешь в ассемблере).
Там ничё сложного. Но я предпочёл всё же получить адрес для бряка программно. А вы мне опять первый оператор да первый оператор
...Вообще сказка про белого бычка. Чтобы знать первый оператор функции надо знать где эта функция начинется чтобы посмотреть её первый оператор чтобы посмотреть где она начинается чтобы...

Короче, вы не знаете.
alex_x_x
бжни
 Аватар для alex_x_x
2441 / 1646 / 84
Регистрация: 14.05.2009
Сообщений: 7,163
13.09.2011, 21:39     Почему глобальный объект, объявленный до main, конструируется в ней? #33
Цитата Сообщение от Evg Посмотреть сообщение
Более правильным было бы сказать "до того, как исполнится первый оператор main'а". Потому как некоторые компиляторы вызов глобальных конструкторов встраивают в самое начало main'а
Цитата Сообщение от Сыроежка Посмотреть сообщение
Нет, это будет неправильно, так как это не соответсвует букве стандарта. Это разработчики компиляторов делают на свой страх и риск!
It is implementation-defined whether or not the dynamic initialization (8.5, 9.4, 12.1, 12.6.1) of an object of
namespace scope is done before the first statement of main. If the initialization is deferred to some point
in time after the first statement of main, it shall occur before the first use of any function or object defined
in the same translation unit as the object to be initialized.31) [Example:
// – File 1 –
#include "a.h"
#include "b.h"
B b;
A::A(){
b.Use();
}
// – File 2 –
#include "a.h"
A a;
// – File 3 –
#include "a.h"
#include "b.h"
extern A a;
extern B b;
int main() {
a.Use();
b.Use();
}
It is implementation-defined whether either a or b is initialized before main is entered or whether the
initializations are delayed until a is first used in main
. In particular, if a is initialized before main is
entered, it is not guaranteed that b will be initialized before it is used by the initialization of a, that is,
before A::A is called. If, however, a is initialized at some point after the first statement of main, b will
be initialized prior to its use in A::A. ]
собственно
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
13.09.2011, 21:55     Почему глобальный объект, объявленный до main, конструируется в ней? #34
Цитата Сообщение от kravam Посмотреть сообщение
Но я предпочёл всё же получить адрес для бряка программно
Я тебе уже объяснял, что адрес функции - это совсем не то. От точки входа в main до printf'а может чёрт-те сколько команд исполниться.

Цитата Сообщение от kravam Посмотреть сообщение
Короче, вы не знаете
Читай пост #29, там всё написано. Если не умеешь работать с ассемблером и не имеешь представления о том, как работает компилятор, то попросту не стоит лазить в отладчике на низком уровне (без отладочной информации).

Цитата Сообщение от kravam Посмотреть сообщение
Так что увы и ах, объект создаётся в maine
Объект вообще создаётся compile-time. А то, о чём ты говоришь, называется "инициализируется". Может статья тебе поможет, но там много букв Конструкторы и деструкторы

Добавлено через 11 минут
И ещё::

Цитата Сообщение от Evg Посмотреть сообщение
А ещё лучше, покажи дизассемблер своей программы
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,271
13.09.2011, 22:49  [ТС]     Почему глобальный объект, объявленный до main, конструируется в ней? #35
Да не. Ничё не даст. Опять тягомотину на пустом месте разводить будешь.

Добавлено через 13 минут
Короче, и смех и грех. Качнул mingw-get-inst-20100831.exe, так вроде нормально всё, сперва создаёт объекты, потом вызывает main, как и задумано. Так ведь он добавляет в код моей проги загрузку вот этой библиотеки: libgcc_s_dw2-1.dll; он с собой приволок её из интернета. Приходится кидать libgcc_s_dw2-1.dll в одну папку с исходником и наблюдать как предсказуемо работает моя прога, удовольствие малоприятное. Так Этот компилятор что, для всех прог будет требовать наличие libgcc_s_dw2-1.dll? ужасно так-то.

Добавлено через 32 минуты
grizlik78, спасибо,тебе, дружище! Может теперь буду почаще менять компиляторы, а то у меня аж с 2004-го года стоял. Поковырялся в переменных среды и всё заработало. Так что спишем всё на старость моего компилятора. Щас поставил 4.5.0. До свидания, друзья. До новых встреч!
alex_x_x
бжни
 Аватар для alex_x_x
2441 / 1646 / 84
Регистрация: 14.05.2009
Сообщений: 7,163
13.09.2011, 22:53     Почему глобальный объект, объявленный до main, конструируется в ней? #36
kravam, писать программы, поведение которых зависит от компилятора - зло
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
13.09.2011, 22:59     Почему глобальный объект, объявленный до main, конструируется в ней?
Еще ссылки по теме:

Глобальный вектор. Почему на выходе этот массив пуст? C++
Правильно созданный глобальный объект C++
Инициализированный объект в main не виден в других функциях C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,271
13.09.2011, 22:59  [ТС]     Почему глобальный объект, объявленный до main, конструируется в ней? #37
Да не, поведение не зависело, вы невнимательно читали тему или вообще не читали её. Просто я мысля обнаружил расхождение между теорией и практикой и решил его решить.
Yandex
Объявления
13.09.2011, 22:59     Почему глобальный объект, объявленный до main, конструируется в ней?
Ответ Создать тему
Опции темы

Текущее время: 01:14. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru