TheCalligrapher большое спасибо за такой развернутый комментарий
Лучше такой объем опускайте под спойлер, чтобы другим читателям было удобнее читать комментарии.
Я постараюсь внести необходимые правки про функции без прототипа, чтобы текст более соответствовал действительности.
ABI позволяют передавать даже variadic аргументы в регистрах процессора.
что в статье и сказано, читайте статью внимательнее, но я согласен, что возможно стоило выразить это в более явной форме
Во-первых, то, о чем вы писали выше, это и есть "альтернативный стиль K&R"
спасибо, это я тоже поправлю, чтобы это было явно указано
Вы это все, конечно, попытались запихать в свою теорию "это всё эллипсис", но это - полная чушь.
Нет, не пытался. Но если Вы меня так поняли, значит я плохо написал
Никакого отношения к языкам С и С++ это, разумеется, не имеет. Никаких calling conventions ни в С, ни в С++ нет. Что это делает в статье про "особенности С и С++" - не ясно.
Полностью согласен, я это писал только для того чтобы дизасм объяснить для printf. Конкретно почему там не через стек толкаются параметры. А то у кого-то могли возникнуть вопросы. Но этот текст нужно либо вообще удалить из статьи, либо опустить под спойлер.
Грубейше не верно. Строковый литерал - это массив, то есть char[N] в языке С и const char[N] в C++
Я ввел в заблуждение, когда обозначил, что в x64 calling convention есть строго-оговорённое количество используемых регистров. Я доработаю этот абзац, чтобы он соответствовал действительности
Спасибо за комментарий. Да деструктор по умолчанию noexcept, как я описал в своей статье.
Ваш пример кода на gobolt это UB код
Подробнее здесь
Это потому что не смотря на то, что вы за счет noexcept(false) может развернуть стек и даже обработать исключение в некоторых случаях (которые, кстати, указаны в статье). Это не значит что он будет работать корректно во всех случаях. Если например вы будете дорабатывать такой код и у вас между созданием и уничтожение такого экземпляра появится какой-то вызов функции, который в теории может бросить исключение ваш код может перестать работать, и вообще может случиться что угодно.
Приведу пример.
Вот код https://godbolt.org/z/hnqTaE7jG. Здесь мы создаем два экземпляра двух типов. Из деструктора B вылетает исключение и он успешно разворачивает стек и ловится (за счет noexcept(false)).
Потом пришел другой программист и начал дорабатывать бизнес логику продукта в этом UB коде и добавил вызов функции после создания экземпляра B. И он это сделал абсолютно легально. Его функция в некоторых случаях может бросать исключение, что также абсолютно легально. Он написал полностью корректный C++ код.
Запустите этот доработанный код https://godbolt.org/z/3YavK5Eah и вы увидите, что он перестал работать. Вряд ли другой программист, мог знать, что Вы кидаете исключение из деструктора. И здесь прав он, его исключение должно было быть перехвачено по правилам языка C++ и обработано. Но вместо этого деструктор A не был вызван, а процесс упал.
Естественно это UB, я не могу дорабатывать и вообще никто не может дорабатывать такой код, в котором оставлены такие мины. При том такие мины бахнут на продакшене уже, в каких-то рандомных кейсах. Это поведение невозможно предсказать. Оно на данном примере ведет себя по разному как мы можем видеть, даже в рамках одного компилятора (то экспешн из деструктора поймался, то не поймался).
Можете уточнить про имена символов или, если есть такая возможность, привести дизасм C кода сбилженого под архитектуру x64 с разными колконвеншенами функций: cdecl, stdcall, fastcall и т.д., где будет видно что имена символов изменились?
Не совсем так. Наличие или отсутствие cdecl в объявлении экспортируемой функции даже для архитектуры x64 как минимум указывает на особенности ее именования в разделяемой библиотеке (dll или so).
Мне кажется это не так или я не правильно понял утверждение. На сколько я знаю, если при билде компилятор игнорирует указанный колконвешн, он и имя никак не будет декорировать согласно указанному колконвешену.
Мои обоснования такие
Потому что в имени как раз и декорируется информация о колконвешене (т.н. декорирование имен). Чтобы во время связывания кода линковщик знал как вызывать функцию и передавать в нее аргументы, а также кто должен сместить адрес стека.
Например линковщик видит символ с таким именем:
@AddSubFc@16
И он понимает из названия (по символу @), что речь идет о конвенции fastcall:
extern "C" int _fastcall AddSubFc(int i1, int i2, int i3, int i4);
Или например видит такой символ:
_AddInts@8
Линковщик понимает что речь идет о такой сигнатуре stdcall:
extern "C" int _stdcall AddInts(int i1, int i2);
Это примеры для MSVC x86.
Я к тому, что если, компилятор игнорирует указанную программистом конвенцию, тогда и имя символа он не будет менять согласно это конвенции, а оставит свою, иначе он не смог бы корректно связаться с символом.
Поправьте меня, пожалуйста, если я не прав или не правильно понял комментарий. Или просто не ясно излагаю мысли
Но я никого не хотел раздражать таким введением или устанавливать правила проведения собеседований.
Это скорее было написано для красного словца. Конечно, же на каких-то собеседованиях про это спорят. Или из любопытства могут спросит, даже глубже могут копнуть, и начнется битва двух ёкодзун.
svlasov, спасибо, уточню это в статье
TheCalligrapher
Большое спасибо, за объяснение, как будет время, внесу необходимые правки
lrrr11 Правильно что не стали читать, не надо было и этот комментарий писать
TheCalligrapher большое спасибо за такой развернутый комментарий
Лучше такой объем опускайте под спойлер, чтобы другим читателям было удобнее читать комментарии.
Я постараюсь внести необходимые правки про функции без прототипа, чтобы текст более соответствовал действительности.
что в статье и сказано, читайте статью внимательнее, но я согласен, что возможно стоило выразить это в более явной форме
спасибо, это я тоже поправлю, чтобы это было явно указано
Нет, не пытался. Но если Вы меня так поняли, значит я плохо написал
Полностью согласен, я это писал только для того чтобы дизасм объяснить для printf. Конкретно почему там не через стек толкаются параметры. А то у кого-то могли возникнуть вопросы. Но этот текст нужно либо вообще удалить из статьи, либо опустить под спойлер.
Что в статье и написано, читайте внимательно.
Верно, это зависит от платформы.
Я ввел в заблуждение, когда обозначил, что в x64 calling convention есть строго-оговорённое количество используемых регистров. Я доработаю этот абзац, чтобы он соответствовал действительности
Спасибо за комментарий. Да деструктор по умолчанию noexcept, как я описал в своей статье.
Ваш пример кода на gobolt это UB код
Подробнее здесь
Это потому что не смотря на то, что вы за счет
noexcept(false)
может развернуть стек и даже обработать исключение в некоторых случаях (которые, кстати, указаны в статье). Это не значит что он будет работать корректно во всех случаях. Если например вы будете дорабатывать такой код и у вас между созданием и уничтожение такого экземпляра появится какой-то вызов функции, который в теории может бросить исключение ваш код может перестать работать, и вообще может случиться что угодно.Приведу пример.
Вот код https://godbolt.org/z/hnqTaE7jG. Здесь мы создаем два экземпляра двух типов. Из деструктора
B
вылетает исключение и он успешно разворачивает стек и ловится (за счетnoexcept(false)
).Потом пришел другой программист и начал дорабатывать бизнес логику продукта в этом UB коде и добавил вызов функции после создания экземпляра
B
. И он это сделал абсолютно легально. Его функция в некоторых случаях может бросать исключение, что также абсолютно легально. Он написал полностью корректный C++ код.Запустите этот доработанный код https://godbolt.org/z/3YavK5Eah и вы увидите, что он перестал работать. Вряд ли другой программист, мог знать, что Вы кидаете исключение из деструктора. И здесь прав он, его исключение должно было быть перехвачено по правилам языка C++ и обработано. Но вместо этого деструктор
A
не был вызван, а процесс упал.Естественно это UB, я не могу дорабатывать и вообще никто не может дорабатывать такой код, в котором оставлены такие мины. При том такие мины бахнут на продакшене уже, в каких-то рандомных кейсах. Это поведение невозможно предсказать. Оно на данном примере ведет себя по разному как мы можем видеть, даже в рамках одного компилятора (то экспешн из деструктора поймался, то не поймался).
chanav, Вы правы, в моей статье ошибка, большое спасибо за данный комментарий!
geher, большое спасибо за комментарий про char
Можете уточнить про имена символов или, если есть такая возможность, привести дизасм C кода сбилженого под архитектуру x64 с разными колконвеншенами функций: cdecl, stdcall, fastcall и т.д., где будет видно что имена символов изменились?
Мне кажется это не так или я не правильно понял утверждение. На сколько я знаю, если при билде компилятор игнорирует указанный колконвешн, он и имя никак не будет декорировать согласно указанному колконвешену.
Мои обоснования такие
Потому что в имени как раз и декорируется информация о колконвешене (т.н. декорирование имен). Чтобы во время связывания кода линковщик знал как вызывать функцию и передавать в нее аргументы, а также кто должен сместить адрес стека.
Например линковщик видит символ с таким именем:
И он понимает из названия (по символу
@
), что речь идет о конвенции fastcall:Или например видит такой символ:
Линковщик понимает что речь идет о такой сигнатуре stdcall:
Это примеры для MSVC x86.
Я к тому, что если, компилятор игнорирует указанную программистом конвенцию, тогда и имя символа он не будет менять согласно это конвенции, а оставит свою, иначе он не смог бы корректно связаться с символом.
Поправьте меня, пожалуйста, если я не прав или не правильно понял комментарий. Или просто не ясно излагаю мысли
Вы абсолютно правы в своих суждениях.
Но я никого не хотел раздражать таким введением или устанавливать правила проведения собеседований.
Это скорее было написано для красного словца. Конечно, же на каких-то собеседованиях про это спорят. Или из любопытства могут спросит, даже глубже могут копнуть, и начнется битва двух ёкодзун.
Большое спасибо, за полезный комментарий! Сразу вспомнился API libpng, в частности то как там обрабатываются ошибки с setjmp