−16
- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
MyBeautifulMarvelousStruc funcWithoutReturnStatement(FuckMyLife eatShitAndDie)
{
MyBeautifulMarvelousStruc mbms;
mbms.myLife = eatShitAndDie;
}
int main()
{
...
MyBeautifulMarvelousStruc test = funcWithoutReturnStatement(eatShitAndDie);
...
}
Призываю дядь в крестах шарящих.
Я объявляю функцию, возвращающую некую структуру. В определении не пишу "return". Собираю с MinGW 4.9.2 32bit. Собираю не вручную, подключил компилер в QtCreator-е. Ошибок при компиляции нет. Создаётся структура, поля которой есть неинициализированный хлам (что не удивительно). Вопросы:
* Что возвращает функция без "return" в теле функции?
* Почему это компилится?
* Что читать, чтобы такие вещи знать?
Не буду утверждать, что читал от корки до корки книжку дядюшки Шилдта, пользовался ей как справочник по базовому синтаксису с комментариями. Листал, короче.
Или это разговор не о c++, а о компиляторах c++?
Запостил:
PascalOverlord,
27 Января 2017
потому что компилятор так захотел, ворнинг то он все равно высрал
что возвратит функция даже сам страуструп не сможет сказать, ибо это UB
чтобы компилятор подавился, а не проглотил это говно, можно ему указать -Werror=return-type
Кароче, если че-та не описано четка в стандарте - это UB. На усмотрение разраба компилера/ОС/черта. Бывают случаи, когда пашет на винде годами, и сразу отказывается компилится на линухе ГЦЦ и прочее. Если тебя не интересуют конкретные извраты, а хочется четкой проверки - включаешь флаг компилера "все варнинги = ошибка" (см. инструкцию компилера).
Это имплементейшон дефайнд. А UB - это невалидный код, бессмыслица (например разыменование невалидного указателя).
Это просто баги реализации стандарта в студии или гцц. UB возникает в рантайме, а не во время компиляции.
Только -Wall -Wextra, только хардкор.
Ну технически это верно: Qt это не стандартный C++ и требует moc-компилятора.
В моем комментарии не было. Ты нафантазировал, не вижу смысла это дальше обсуждать.
Всякий мусор. Зависит от колконвеншена. Это UB, так делать никогда нельзя.
>>Почему это компилится?
Так написано в стандарте
>>Что читать, чтобы такие вещи знать?
Стандарт
Студент, гугли про calling convention (уж про регистры, стек, кучу ты должен в своем возрасте знать), узнай какой используется в выбранном тобой языке (/компиляторе/декларации конкретной функции), поймешь где лежат данные, которые интерпретируются вызывающим как результат интересующей тебя функции - источник мусора можно определить уже с высокой точностью
Потом гугли методы, которыми выбранный тобой компилятор оптимизирует код, особенно кейсы, когда ему подали не очень валидный код (UB) - в этом случае он имеет право вообще часть твоих инструкций выкинуть к ебеням, потому что в них нет смысла, а мусор результата в результате такой оптимизации найти в другом месте, поближе. Даже на говнокоде есть много постов о таких неожиданных оптимизациях.
В итоге же полный и четкий ответ даст ассемблерный листинг твоей программы и конкретно куска, где был вызов. Есть даже инструменты онлайн - gcc.godbolt.org. В любом случае этот мусор вполне детерминирован, т.к. процессор не может сам нагенерить тебе мусор, он слишком бездушный ублюдок, он просто исполняет то, что ему дают.
- Ага...
- Это неправильный hello world, чтобы написать правильно иди кури стандарт, учи асм и выхлоп компиляторов..."
> В любом случае этот мусор вполне детерминирован
Так можно и в стекфрейм после другого приложения залезть
Э, как?
Или я дальтоник и не увидел зелёный?
Я думал, что такое только в DOS бывает...
Назвали бы тредами, и никто бы не докопался :)
В самую писечку угадал.
Было приложение, отработало, ракрылось. Память никто не занулял. Потом приходит второе приложение, садится на тот же стек и не найдя ни мобильного устройства, ни инструкции к туалетной бумаге, начинает смотреть на то, что высрало после себя уже завершившееся приложение.
и ось сразу метнулась тушканчиком их всех занулять
Memory from the heap is returned back to the system when it is unmap'd or the heap shrinks with sbrk(). At this point pages can be re-used by other processes and when they are re-used they are always zero'd before the process can access it.
Молодец какой!
Руссинович грит и винда не без этого:
"Firstly, the zero page thread runs at the lowest priority and is responsible for zeroing out free pages before moving them to the zeroed page list."
Хочу заметить, что память зануляется не после того, как страница возвращается системе, а перед тем, как система отдает страницу процессу.
Во-вторых логично что эта штучка лениво работает: зачем заранее зануляться, если эта память никогда не понадобится?
Особенно когда sigkill прилетает.
> Во-вторых
То была ремарка к твоему саркастичному комментарию "и ось сразу метнулась тушканчиком...".
да, аргумент.
>>То была ремарка к твоему саркастичному комментарию
Я уже понял что обос^W^Wзаблуждался.
Вот только х.з., есть ли там тред для зануления или зануление будет только во время copy-on-write...
1) сводил к минимуму фрагментацию памяти
2) работал бы ОЧЕ быстро
например, free не возвращает сразу память системе чтобы не дерагать лишний раз сискол (sbrk или как там оно в линуксе).
Ребят уровня фейсбука и гугла это может не устраивать
IIRC, во время COW (copy-on-write). смысла мало обнулять физ память которая может быть и не будет использоватся для памяти приложений. на линухе файло-диско-кэш резиновый, и потребляет часто больше памяти чем приложения.
> что у линухи вообще один r/o zero page на всех.
ага. /dev/zero из этой же странички данные и читает. неиссякаемый источник нулей...
WorkingSet это память в RAM, ЕМНИП, а вся вместе называтеся VIRTUAL
>> * Что возвращает функция без "return" в теле функции?
>> * Почему это компилится?
> гугли про calling convention
> источник мусора можно определить уже с высокой точностью
А мне кажется, вопрос был не про сорта мусора, а про идеологию; что автору поста как-то пофиг на конкретные байты, в которых, разумеется, что-то несвежее осталось.
* Какого хрена компилятор в языке с якобы строгой типизацией не бугуртит ошибками на такое разгильдяйство?
* Если это компилируется, то какая суть у возвращаемого значения? (JS без return возвращает значение специального типа)
Ну по крайней мере, я бы такие вопросы задал, если бы мне было не лень создавать новый пост.
В доисторические времена эта 'фича', видимо, была запилена для случаев, когда выход из функции недостижим (всегда выходят раньше или какой-нибудь бесконечный цикл).
причем тут типизация?
Предвосхищая вопрос "причём тут строгая типизация?", напишу:
Возвращается значение определённого типа, поэтому и строгая типизация.
Иными словами, неявный каст void в произвольный тип - питушня, противоречащая идеологии строгой типизации.
А что там внутри функции делают комплеятор знать не обязан.
А вот что в дефишинеше написано что надо что-то вернуть, а возвращают ничего -- это фейл, но типизация тут не причемю
Охуенно, форсим поцаны!
Будешь форсить -- я тебе лицо причемю
> не причемю
В каком месте там каст?
Жрет кактус, срет ворнингами. Это наследие си просто
Он в своей книге излагает, скорее всего как тётушка
Кстати, у Watcom'а есть удобная директива #pragma aux, в которой можно явно указать, кто будет выделять память под возвращаемую структуру: вызывающий код или вызываемая функция.
Т.е. можно совсем другое соглашение описать - набор регистров и т.п.? Или только немножко подтюнить стандартные?
Более того, стандартные соглашения тоже где-то описаны:
Кстати:
The following form of the auxiliary pragma can be used to describe a function that does not return to the
caller.
Ну это же банальный noreturn...
(при использовании «регистровой» версии библиотек)
(при использовании «стековой» версии библиотек)
(не описано; в справке приведено в качестве примера линковки с «чужими» модулями)
Мне нравится. Будем форсить?
4 января сего года был коммит.
Ватноком — тёплый, ватный, белый. Проверь.
У нас есть реальный шанс зафорсить что-то новое.
Теплый, ламповый, твой *
-----------------------
* Поддержка стандартов C++17 и C11 заблокирована по решению органов государственной власти.
Maintenance time is being implemented to allow for development and testing. The server will be offline during the following hours:
Ночью просто маме телефон нужен..
Внезапно:
https://en.wikipedia.org/wiki/C11_(C_standard_revision)#Criticism
The open-source Open Watcom C/C++ contains a "Safer C" library that is considered a nearly conforming implementation.
Т. е. ко-ко-конпелятор C11 не поддерживает, но библиотека на всякий пожарный с C11 совместима.
напомнило о старой доброй проблемке (тьюринговских машин): а завершится ли программа, или нет?
с другой стороны, все компилеры поддерживают варнинги для этого. я уже давно без -Wall ничего не компилю. (правда на крестах всегда отключаю -Wreorder потому что это маразм.)
> Причем тут время жизни?
> Если бы там были шаредпойнтеры, ничего бы не изменилось.
> Сначала надо создать иосервис и контекст, а потом стрим.
В этом конкретном примере тебе просто повезло, что всех объектов по одной штуке. Но обычно сокетов много, а иосервис - один на всех. Да и ssl::context реюзабельный. И вот там управление лайфтаймом ссылок уже не так тривиально (кроме, пожалуй, "засуну их все в глобалки, да и хуй с ними").
> ничего бы не изменилось
> сначала надо создать иосервис и контекст, а потом стрим
Если бы там были шаред поинтеры - я бы мог запилить всё это по-отдельности внутри конструктора (или взять уже готовые). Ссылки заставляют меня делать всё это в инициализаторе. Ссылки заставляют меня тащить зависимости в инициализатор (ну кроме совсем-совсем глобальных). У меня нет выбора. Я обязан иметь все необходимые объекты к моменту вызова инициализатора.
самое простое & straightforward решение - динамика:
или, если жмет, тогда как ты делал + ревью.
> this->moe = new moe;
Фублядь, фунахуй! Этот код же течёт как сучка весной... Такое я бы на ревью точно не пропустил (хоть у нас и нету исключений, лол).
PS с другой стороны, запихиваешь это в `bool Bormand::Init()` и все пучком. так или иначе ошибки конструкции/инициализации обрабатываеть надо. я предпочитаю их явно и без исключений получать.
P.s. на крестах не пишу
Какой тогда выход?
1) не использовать new
2) не использовать конструкторы
3) на один new одна обертка?
Смартпоинтеры - это вариант 3 (на один new одна обёртка), тащемта.
Как-будто его кто-то позовёт...
я стараюсь делать легкие кторы + bool Init() методы. потому что из ктора значения вернуть нельзя. и не виртуальный он. а с обычным методом инициализации можно делать все что хочешь.
А ты уверен, что понимаешь семантику конструирования объектов? Знаешь, что происходит с первым сконструированным "полем", если конструктор второго "поля" кидает исключение?
Честно говоря, меня очень удивляет, что С++-погромисты не знают таких вещей. Конструкторы и деструкторы — cамая полезная фича языка, и 95% погромистов не знает, как этим пользоваться.
о, нет, знаю: на кресты и их компилеры полагатся ни в чем нельзя. (как и на десктопных макак которые компилеры раз в неделю меняют.)
буду рад если просветишь, потому что на стандарт все равно забил, и самый свежий компилер под рукой это гцц 4.8.
Если в конструкторе по какой-либо причине происходит исключение, всё уже сконструированные поля автоматически разрушается в порядке обратном тому, в котором они были сконструированы. Деструктор конструируемого объекта при этом, понятное дело, не вызывается. Это позволяет писать простой, понятный и корректный код без портянок try/catch. Поэтому порядок членов в классе и инициализаторов в конструкторе важен.
Всё это работало ещё в сраном сfront-е и никогда не изменится — это суть языка. Почитай Inside the C++ Object Model на досуге, на редкость занимательная книжица.
это я когда то слышал. класная теория. если бы только у меня практического опыта который говорит что это не всегда/не везде работает, то я бы с радостью этим пользовался. лично как-то кторы переписывал потому что память текла, потому что деструкторы полей не вызывались. и я это нашел только потому что в теории не верю - только в практику.
ЗЫ о. ты мне напомнил. видел еще грабли когда конпилер порядок инициализации полей менял. вставляешь в конструкторы принтфы - работает как надо. удаляешь принтфы - криво. но это очень древняя история со времен гцц 2.95/билдер 5.5/vc++6.
Обжёгся на молоке, дует и на воду.
На твоём компиляторе не работает? Указатели тоже на nullptr проверяешь перед тем, как удалить? А результат оператора new?
Ты пословицу изкаверкал. "Раз молоком обжёгся - на воду дуешь". 10+ лет работы с недоделаными компилерами - включая и текущий момент - это не "раз".
Я бы рад новыми компилерами пользоватся - компилерами на которые можно полагатся - но гамно на текущем проекте даже темплейтов не умеет (IAR 5.10/5.50).
с точки зрения gcc, 3.х слегка сосали. 4.х, С++03 и частично С++11, где-то только в 2008-10 году стабилизировались. 5.1 вышел в 2015 и был первым гцц где С++11 был официально полноценно поддержан.
а народ тут повествует как если бы щасце неглючных компилеров наступило вечность назад.
То, о чём я говорю, относится к стандарту 98 года.
... который только до конца в 2003 допилили.
но какая в ж разница какой год если ни в 98 ни в '03 не было ни одного компайлера который 100% этого стандарта не умел.
Так бы и сказал, что у тебя не С++, а си с классами, и компилятор, не поддерживающий даже стандарт 98 года.
То, что когда-то компиляторы были плохими, не означает того, что не нужно знать базовую семантику языка, на котором пишешь.
Держите плюсик. Я бы кружечку горячего чаю/какао вам налил в знак соболезнования.
Если оно nothrow - куда деваться...
ну не надо о больном то...
ЗЫЫ вообще, в языке есть всего две оптимизации, позволяющие менять наблюдаемое поведение: RVO и copy elision. А "вставил printf - заработало" - это обычно не в компиляторе дело, а какой-то метод стек портит (возможно, из-за некорректных соглашений о вызовах)
нет. там были просто глюки. деструктор уже сконструированого под-объекта просто не вызывался, почему и память не освобождалась.
эффект принтфа был в том что он предотвращал инлайнинг конструкторов. без инлайнов - код генерился правильный. заинлайненый - кривой.
assert(IsInitialized()) в каждый метод вставляешь?
с другой стороны, по личному опыту, эта проверка смысла мало имеет, потому что кривой this редко бывает NULL.
Да, приходится. Исключений не от хорошей жизни нету :(
Тут хотя бы джвухфазная инициализация скрыта от юзера. И невалидный объект не запилишь.
Правильное, имхо, поля сделать смартпоинтерами...
Это да, но не всегда возможно. На собесе обычно явно говорят, что члены класса менять нельзя.
Нормально. Это одно из типовых решений.
А если у этих объектов деструктор нетривиальный?
А теперь настрой мне waifu (добавь туда сертификатики CA, к примеру) перед конструированием kawaii.
А потом прикрутим боровчекер к шлангу, и будет у нас говнораст.
Теперь по коду гуляют джва инстанса "синглтона" (один из них скоро сдохнет, но не сразу).
З.Ы. Или там какой-то дополнительный семафорчик есть, а не только weak_ptr?
З.З.Ы. Всё, пора спать, дополнительный семафор там однозначно есть. Ибо сами shared_ptr и weak_ptr нельзя юзать из джвух потоков.
Ткни меня носом, если я не прав.
Но вот сам шаред птр не потокобезопасен. И в джва треда без лочки/атомиков с ним работать нельзя.
А атомиков для weak я что-то не нашёл (только для шаред).
История из жизни: ревьюил разок код, где хотели реализовать "горящее" обновление конфигурации процесса при получении SIGHUP. Конфиг положили в глобальный shared_ptr, который читался тредами-воркерами и время от времени перезаписывался новой версией асинхронно тредом, слушающим сигналы.
Люди почему-то решили, что shared_ptr "потокобезопасный", и можно делать такие вещи без всяких мутексов. Так вот — нельзя. Можно безопасно копировать shared_ptr из разных потоков. А вот чтение в одном потоке объекта, который изменяется в другом без внешней блокировки — это точно такой же датарейс.
Здесь грабель нет. Грабли появляются, когда ты кладёшь новый объект в shared_ptr без мутекса.
Глупо, имхо. Нафиг на ровном месте грабли раскладывать? Конпелятор же один хуй сделает по-своему (в порядке объявления полей, забив на порядок инициализаторов в конструкторе). А без этого ворнинга кто-нибудь рано или поздно не посмотрит на порядок членов в классе и обосрётся с конструктором.
если у тебя ктор не зависит от порядка инициализации - все делается явно - то и граблей никаких нет.
знаю что в крестах есть фанаты референсов, которые без этого просто жить не могут. но это те же самые идиоты которым либо лень один символ больше напечатать, либо наивно думают что так как нет '->' то код будет быстрее потому что нет дереференса.
вообщем - маразм.
Я согласен. Но если кто-то по-неопытности (или по-необходимости) въебёт зависимость - лучше уж ворнинг, чем молчаливый UB.
А ворнинга про "зависимость от порядка инициализации" то нету, надеешься всё это отбить на ревью?
да. архитектить/дезайнить компилеры все таки еще не умеют. ;)
я всегда пытаюсь кторы легкими делать. и по граблям зависимостей инициализации ходил - как и по граблям тормозных кторов. (у меня был как-то раз плачевный опыт когда объект ключа для хэша (доморощеного) создавался в среднем дольше чем вычесление ключи + поиск в самом хэше.)
> идиоты
Вот это предъявы. Обоснуй.
> лень один символ больше напечатать, либо наивно думают что так как нет '->' то код будет быстрее
Про то, что ссылки используют, потому что они безопаснее, ты не слышал конечно же?
чем они безопастнее?
когда увижу хотя бы один проект где не будут из указателей референсы делать (и не будет `if (&ref != NULL)`) я может быть и поверю в это преславутую безопастность.
Тут неоднократно обсуждали, что компилятор делает с такими if'ами (https://godbolt.org/g/2EHGbO).
о какое щчастье. другими словами, референсы говно по дезайну, с какой стороны на них не смотреть.
PS проверку дропает только начиная с гцц 6. в пятерке она еще генерится. где дискусия почему проверку дропают?
> почему проверку дропают
Потому что ссылка не может быть нулевой. Ты ведь хороший программист и не разыменовывал нулевой указатель? Это UB!
А моя иде сама меняет . на -> даже когда не прошу блеать
референс нужен в т.ч. для адекватной семантики копирования. В этом нет ничего зазорного
> а более глупый не развернул константы, думает что она достижима
Бывает и хуже: http://govnokod.ru/12472
потому что в старые времена тупой главный цикл - int main() { while(1) {}; } - уже кидал этот ворнинг.
З.Ы. ИРЛ я всё-таки не такой упоротый, как тут.
самые упортые на форумы не ходят. что бы компромисы искать - надо разговаривать. а если слишком долго разговаривать, то коллеги могут начать догадываться какой дебил. вообщем, коммуникация, как обычно.
Если окажется что и там никто не умеет программировать, то придется признать что все люди -- идиоты и тебя спасет только стартап.
-----
зы: я тоже иногда бугурчу. Мне кажется что все кругом пишут тупой бойлерплейт, да еще и не документированный. А должны писать тонкие и красивые фреймворки, которые сами будут решать все проблемы, а потом еще их документировать так, чтобы я прочитал гайд, и всё понял а не сидел в дебагере 8 часов
Передай своим коллегам, что они анскиллы. Над тру интерпрайзом ты в дебаггере не посидишь -- хартбиты поедут и всё ребутнётся вместе с твоим дебаггером.
на самом деле про дебагер это я конечно загнул. Иногда достаточно 8 часов почитать код и всё становится понятно.
Да тут скорее не "не умеет", а "программирует не так, как я".
У всех свои профдеформации, везде свои гайдлайны, да и специфика проекта порой ограничения накладывает.
Молодой программист должен считать говнокодерами всех, кто программирует не так, как он.
- набраться харизмы и терпения
- убедить всех, что они - тупые мудаки
- постоянно тыкать их носом в насранное и возюкать, возюкать, как котят
- конфликты игнорировать, уходить с работы вовремя, вкусно кушать и вволю спать
- гуси, ответный звонок, это всё
та спокойно нужно относиться
все косячат, у всех какие-то тараканы; вполне возможно, более чем обоснованные
В конце, решения должны принимать те, кто несут ответственность за конечный результат.
"Как справляться с такими рабочими ситуациями?"
С друг другом разговарить. Учится у друг друга.
Если не способен учится - либо садить в угол и давать ему ручные тесты делать, либо в начальники промотить.
Когда такое было последний раз коллегу уволили. <пафос>Моё мнение - эталон</пафос>
не верю. но линуха под рукой потестировать нет. память для возващаемого объекта выделяется на стеке - и выделяется она всегда. отсутствие/присутствие return'а в С/С++ никогда проблемой не было, потому что calling convention не пасцалева.
http://coliru.stacked-crooked.com/a/5eacd8836d69a3dd
http://coliru.stacked-crooked.com/a/88fa5a7c5df2f583