Форум программистов, компьютерный форум, киберфорум
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 17, средняя оценка - 4.82
kravam
быдлокодер
1702 / 889 / 45
Регистрация: 04.06.2008
Сообщений: 5,499
#1

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

13.09.2011, 17:16. Просмотров 2226. Ответов 36
Метки нет (Все метки)

Всё просто, имеем код:
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++. Спасибо, кто откликнется.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
13.09.2011, 17:16
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Почему глобальный объект, объявленный до main, конструируется в ней? (C++):

Почему ругается на не объявленный идентификатор - C++
#include&lt;iostream&gt; #include&lt;time.h&gt; #include&lt;stdio.h&gt; #include&lt;conio.h&gt; using namespace std; int main() { int...

Глобальный объект - C++
Как сделать, чтобы я создал объект, и его видел весь файл? Суть в том, что в конструктор я хочу передать аргумент. Или нужно сделать...

Глобальный объект класса - C++
Добрый вечер всем!) Вообщем суть такая: пишу статическую библиотеку, которая состоит из кучи файлов. Есть класс, конструктор которого...

Динамический глобальный объект. - C++
Здравствуйте. Пишу Auto-Clicker . Текущая версия содержит заранее созданный массив объектов класса &quot;TClickBase&quot; в каждом объекте хранятся...

Глобальный объект класса - C++
Ребят, написал класс для реализации очереди на основе массива в отдельном юните, теперь мне нужно в другом получить объект класса, который...

Правильно созданный глобальный объект - C++
Необходимо создать создать несколько объектов классов глобально. Я делаю: Создаю h-файл. в нем объявляю(создаю) объекты. И в срр -...

36
kravam
быдлокодер
1702 / 889 / 45
Регистрация: 04.06.2008
Сообщений: 5,499
13.09.2011, 18:34  [ТС] #16
Цитата Сообщение от grizlik78 Посмотреть сообщение
Что касается отладчика, то на мой взгляд (это лишь моё мнение, да) отладчик останавливается до вызова конструктора не по ошибке, а как раз чтобы была возможность отследить, какие объекты инициализируются до начала программы. Но я не знаю этого наверняка.
Отладчик не обязательно отражает истинное положение вещей. В ассемблерный код посмотреть надёжнее всего. Пожалуй, пойду посмотрю.
Отладчик останавливается на точке входа и на бряках. Кто не верит- пожалуйста, hiew к вашим услугам. НАйдите её вручную и сравните с тем, на чём стоит OllyDbg. И в отладчике да, именно ассемблерный код.

Добавлено через 1 минуту
Цитата Сообщение от Сыроежка Посмотреть сообщение
Нет такого понятия, как "глобальный объект относительно какой-нибудь функции". Есть глобальное пространство имен, и все объекты, расположенные в этом пространстве, называются глобальными. Кстати сказать, не надо путать глобальное пространство имен с неименованным пространством имен!
хорошо, хорошо, я понял. Ну так делать-то мне что? По разным исполняемым кодам функции разнести или что?
0
Evg
Эксперт CАвтор FAQ
18265 / 6389 / 440
Регистрация: 30.03.2009
Сообщений: 17,676
Записей в блоге: 28
13.09.2011, 18:38 #17
Цитата Сообщение от Сыроежка Посмотреть сообщение
Нет, это будет неправильно, так как это не соответсвует букве стандарта
Насколько я знаю, стандарт не учит разработчиков, как писать компиляторы. С точки зрения пользователя, как уже писалось, нет разницы. Ну и до кучи если есть возможность, приведи выдержку из стандарта.

Цитата Сообщение от Сыроежка Посмотреть сообщение
Не только по-моему, но и в соответствии с языками С и С++ точкой входа функции является адрес этой функции, который устанавливается при редактировании связей и формировании загрузочного модуля
Я спрашивал о "точке входа", а не о "точке входа в функцию", а потому и спросил, что он имеет в виду. "Точкой входа" обычно называют точку запуска приложения (функцию _start или как-то по -другому оно моджет называться в разных системах)

Цитата Сообщение от Сыроежка Посмотреть сообщение
Точка входа у модуля одна
Что такое "точка входа модуля"?

Цитата Сообщение от kravam Посмотреть сообщение
Я нашёл противоречие между теорией, которая гласит, что если объект глобальный относительно какой-нибудь функции, то вне её он и должен конструироваться и практикой. Хочу Это противоречие разрешить. Просто стараюсь добросовестно учиться только и всего.
Ятебе уже говорил, что вставать надо не на начале функции main, а на первом операторе. Или, скомпилив код сотладочной информацией, это за тебя сделает отладчик: т.е. ты его попросишь остановться в main'е, но он поставить брекпоинт на первом операторе.

Напомню так же, что printf - это буфферизуемый вывод, а потому гарантированно ты его увидишь только по завершении исполнения программы. Если где-то в середине работы программы ты не видишь выдачи, это не значит, что printf не отработал. Честным было бы завести глобальную переменную, статически инициализировать её нулём (что будет сделано по дефолту), в конструкторе увеличить значение этой переменной, а на точке брекпоинта посмотреть значение переменной
0
Сыроежка
Заблокирован
13.09.2011, 18:42 #18
Цитата Сообщение от Evg Посмотреть сообщение
Насколько я знаю, стандарт не учит разработчиков, как писать компиляторы. С точки зрения пользователя, как уже писалось, нет разницы. Ну и до кучи если есть возможность, приведи выдержку из стандарта.
Стандарт не учит писать компиляторы. Он учит тому, что можно называть языком программирования С или С++.
Что касается выдержки из стандарта, то у меня сейчас нет такой возможности ее предоставить. Я опирался на свою память.
0
grizlik78
Эксперт С++
1967 / 1460 / 120
Регистрация: 29.05.2011
Сообщений: 3,022
13.09.2011, 18:43 #19
Цитата Сообщение от Evg Посмотреть сообщение
Напомню так же, что printf - это буфферизуемый вывод, а потому
О, а std::cerr, вроде, по-умолчанию без буферизации? Можно его попробовать. Хотя у kravam и компилятор-то довольно древний. Чёрт знает что там может твориться.
0
Evg
Эксперт CАвтор FAQ
18265 / 6389 / 440
Регистрация: 30.03.2009
Сообщений: 17,676
Записей в блоге: 28
13.09.2011, 18:46 #20
Цитата Сообщение от grizlik78 Посмотреть сообщение
О, а std::cerr, вроде, по-умолчанию без буферизации? Можно его попробовать. Хотя у kravam и компилятор-то довольно древний. Чёрт знает что там может твориться.
Да. Без буфферизации. В варианте kravam'а это будет замена "printf" на "fprintf (stderr". Хотя опять-таки чёрт его знает, что там будет с выдачей приработе из-под отладчика, терминал, как-никак, это внешнее устройство, которое работает независимо от программы. А глоабльная переменная - это часть программы
0
kravam
быдлокодер
1702 / 889 / 45
Регистрация: 04.06.2008
Сообщений: 5,499
13.09.2011, 18:56  [ТС] #21
Цитата Сообщение от Evg Посмотреть сообщение
Ятебе уже говорил, что вставать надо не на начале функции main, а на первом операторе.
Я записал, На бумажку на всякий случай, чтобы не было недоразумений. Вот как вы сказали:
Цитата Сообщение от Evg Посмотреть сообщение
По честному брекпоинт надо ставить на первый оператор main'а
По моему первый оператор main и есть начало main. Не? Если не так, уточните понятия, как это сделал я.
За printf: как бы не хотелось мне думать, что я просто "не заметил" вызова конструктороа, это не так. Брякаюсь на начало main (первый оператор или как его там), пошаговто трассирую и попадаю на printf с параметром "konstruktor". Так что увы и ах, объект создаётся в maine, что ставит с ног на голову мои и без того шаткие знания. Ребята, может в опциях компилятора надо что поправить? Я щас это смотрю уже.
0
Evg
Эксперт CАвтор FAQ
18265 / 6389 / 440
Регистрация: 30.03.2009
Сообщений: 17,676
Записей в блоге: 28
13.09.2011, 19:18 #22
Цитата Сообщение от kravam Посмотреть сообщение
По моему первый оператор main и есть начало main. Не?
Это не так. В начале процедуры main (с точки зрения бинарного кода и отладчика), как и в начале любой процедуры идёт так называемый пролог. Как правило это настройка стека, настройка указателей стека и прочих вещей, о которых пользователю знать незачем, потому как в языке программирования нет этих понятий (на то он и язык, чтобы не опускаться до уровня системы команд, программных соглашений и т.п.)

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

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

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

Добавлено через 1 минуту
Т.е. пост #4
0
kravam
быдлокодер
1702 / 889 / 45
Регистрация: 04.06.2008
Сообщений: 5,499
13.09.2011, 19:35  [ТС] #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 на корню зарубает мою тягу к знаниям.
0
grizlik78
Эксперт С++
1967 / 1460 / 120
Регистрация: 29.05.2011
Сообщений: 3,022
13.09.2011, 19:43 #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 не обнаружено.
0
kravam
быдлокодер
1702 / 889 / 45
Регистрация: 04.06.2008
Сообщений: 5,499
13.09.2011, 19:58  [ТС] #25
У меня 3.4.2, можешь скинуть куда-нибудь?

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

Цитата Сообщение от kravam Посмотреть сообщение
как понять брейкпонт на имя? Тебе отладчик подсказывает, где находится имя?
GDB без оболочек является консольным отладчиком. Командами управляется.
То есть я имел в виду команду
Код
break main
0
kravam
быдлокодер
1702 / 889 / 45
Регистрация: 04.06.2008
Сообщений: 5,499
13.09.2011, 20:20  [ТС] #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 не получить!
0
grizlik78
Эксперт С++
1967 / 1460 / 120
Регистрация: 29.05.2011
Сообщений: 3,022
13.09.2011, 20:40 #28
Цитата Сообщение от kravam Посмотреть сообщение
Час от часу не легче, оказывается, на main нельзя брать адрес
Да, об этом я уже сказал, но это не выдумка Microsoft, это описано в стандарте C++. Впрочем, к GCC это не относится, он разрешает получение адреса и рекурсивный вызов main().

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

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

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

Нечеловеческий способ - это продизассемблировать программу и глазками посмотреть адрес, который будет соответствовать первому оператору (если понимаешь в ассемблере). В режиме с оптимизациями ты этого в принципе уже получить точным образом, потому как компилятор скорее всего коды перемешает и не факт, что в программе найдётся точная граница между тем, как "первый оператор уже выполнен" и "первый оператор ещё не выполнен", потому что это на языке только один оператор, а в коде это будет несколько машинных инструкций, которые могут перемешаться с инструкциями другого оператора
0
grizlik78
Эксперт С++
1967 / 1460 / 120
Регистрация: 29.05.2011
Сообщений: 3,022
13.09.2011, 21:13 #30
Цитата Сообщение от Evg Посмотреть сообщение
я так понимаю, что у тебя отладчик gdb
Так вроде ж OllyDbg у него. Фиг его знает, как там с интеграцией с кодом от MinGW. Это я GDB мучил.
0
13.09.2011, 21:13
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
13.09.2011, 21:13
Привет! Вот еще темы с ответами:

Почему в одних случаях пишут void main(), а в других случаях int main() {return 1;} - C++
Препод мне доказывает, что void в main`е писать неправильно, а объяснить не может почему. Кто расскажет?)

Инициализированный объект в main не виден в других функциях - C++
void display() { clear(); rocket.draw(); //ОШИБКА glFlush(); glutSwapBuffers(); _time++; } int main()

Глобальный вектор. Почему на выходе этот массив пуст? - C++
юзаю либу вектор. почему на выходе этот массив пуст? std::vector&lt;int&gt; qw;//global int main() { int i=1; qw.push_back(i); ...

Как передавать объект класса в методе main, чтобы конструктор не вызывался 2 раза? - C++
Проблемы заключаются в следующем: есть класс для создания записи и класс для хранения массива этих записей. Так вот, как передавать объект...


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

Или воспользуйтесь поиском по форуму:
30
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru