LINUX.ORG.RU

Тёмные углы C и C++

 ,


13

8

Изначально даный пост предназначался для habrahabr, но местные модераторы его не пропустили без объяснения причин:

Увы, ваш топик «Тёмные углы C и C++» не был одобрен и не попал в общую ленту. Причин этому может быть много, но просим не терроризировать службу технической поддержки, так как там вашу статью даже не видели. Расстраиваться тоже не стоит - попробуйте опубликовать что-нибудь еще.

С уважением, Хабрахабр

В связи с этим выкладываю его сюда.

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

Я не гуру C++. Я не могу похвастаться тем, что с уверенностью распознаю любую шаблонную конструкцию данного языка или возьму и вспомню, что возвращает какая-то редко используемая мной функция из стандартной библиотеки без помощи документации.

Думаю, вы со мной согласитесь, что C++ — язык с очень высоким порогом вхождения. Серьёзно! Я изучаю этот язык уже больше 3 лет, и практически каждую неделю открываю в нём что-то новое и удивительное. Именно об этом «новом и удивительном» и пойдёт речь в данной статье.

За то время, пока я изучал C++, у меня потихоньку накапливались интересные задачки, сниппеты и просто необычные куски кода, которыми я делился с коллегами по работе и знакомыми. У нас даже появилась своего рода традиция — каждый рабочий день с того момента, как я пришёл в компанию, я выкладывал по две новых задачки, которые мы старались по возможности обсудить в перерывах. Постепенно их набиралось всё больше и больше, пока, наконец, я не стал забывать некоторые из них. Именно в тот момент я решил предпринять попытку составления сборника так называемых «тёмных углов» C и C++. Я собрал воедино все те куски кода, что видел до сих пор, дополнил их цитатами из различных стандартов и продолжил копить свою «коллекцию». Изначально моей целью было собрать вместе всего 100 задачек, но я и оглянуться не успел, как их стало уже 200, 300 и вот теперь 400. На самом деле, их даже больше, но на данном этапе я решил ограничиться именно этим количеством.

Итак, представляю Вашему вниманию книгу «C and C++ Dark Corners». Конечно, назвать её книгой можно с натяжкой, ведь это, как я уже и сказал, сборник интересных и для кого-то малоизвестных мест C и С++.

Не откладывайте её в сторону после пары-тройки вопросов, ответы на которые Вы уже итак знаете — я уверен, что хоть какие-то из них заставят Вас задуматься, а ответы на другие могут удивить и даже шокировать.

В качестве примера приведу несколько вопросов из книги:

97. Что попадёт в stdout в результате выполнения след. кода и почему?

#include <iostream>
#include <memory>

#include <boost/smart_ptr/scoped_ptr.hpp>

class Foo
{
public:
 ~Foo() { std::cout << "Foo::~Foo() \n"; }
};

class Bar : public Foo
{
public:
 ~Bar() { std::cout << "Bar::~Bar() \n"; }
};

class Baz : public Bar
{
public:
 ~Baz() { std::cout << "Baz::~Baz() \n"; }
};

int main()
{
 std::cout << 1 << '\n';
 {
  Foo* instance = new Baz;
  delete instance;
 }

 std::cout << 2 << '\n';
 {
  std::shared_ptr<Foo> instance(new Baz);
 }

 std::cout << 3 << '\n';
 {
  std::shared_ptr<Foo> instance(false ? new Bar : new Baz);
 }

 std::cout << 4 << '\n';
 {
  boost::scoped_ptr<Foo> instance(new Baz);
 }

 std::cout << 5 << '\n';
 {
  std::unique_ptr<Foo> instance(new Baz);
 }

 std::cout << 6 << '\n';
 {
  std::auto_ptr<Foo> instance(new Baz);
 }
}

A: Первый случай уже обсуждался ранее – здесь UB в чистом виде (как правило, это приводит к тому, что не будет вызван деструктор производного класса).

Второй случай выдаст на экран следующее:

Baz::~Baz()

Bar::~Bar()

Foo::~Foo()

Почему? Что произошло? Ведь мы же ясно видим, что деструкторы у данных классов не являются виртуальными. Или это один из частных случаев UB? На самом деле, тут всё вполне законно и должно работать так, как указано выше. Посмотрим в документацию к std::shared_ptr и boost::shared_ptr:

http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr

Proper delete expression corresponding to the supplied type is always selected, this is the reason why the constructors are implemented as templates using a separate parameter Y.

http://www.boost.org/doc/libs/1_51_0/libs/smart_ptr/shared_ptr.htm

This constructor has been changed to a template in order to remember the actual pointer type passed. The destructor will call delete with the same pointer, complete with its original type, even when T does not have a virtual destructor, or is void.

Третий случай выдаст:

Bar::~Bar() Foo::~Foo()

Почему? Ведь мы только что обсуждали поведение std::shared_ptr! Что тут не так? Вспомните поведение тернарного оператора в C++ — он требует, чтобы второй и третий его операнды были одинакового типа. Более того, каст от базового к производному касту без лишних действий не выполнить, в отличие от обратной ситуации:

#include <iostream>

class Base
{
};

class Derived : public Base
{
};

int main()
{
 Base* first;
 Derived* second;

 Base* foo = second; // Ok
 Derived* bar = first; // Error: invalid conversion from 'Base*' to 'Derived*'
}

Наткнуться на данный момент, связанный с тернарным оператором, можно также и в подобном случае:

#include <iostream>
#include <typeinfo>

template <typename T, typename U>
auto foo(bool b, T t, U u)-> decltype(b ? t : u)
{
 return b ? t : u;
}

int main()
{
 auto _1 = foo(true, 0, 'c');
 std::cout << typeid(_1).name() << '\n';
 auto _2 = foo(false, 0, 'c');
 std::cout << typeid(_2).name() << '\n';
}

Переменные _1 и _2 будут одного и того же типа – int.

Именно поэтому Baz в данном случае будет приведён к Bar.

4, 5 и 6 случаи ничем не отличаются от первого – здесь UB. Надо помнить, что поведение std::shared_ptr отличается от остальных умных указателей.

43. Зачем может понадобиться писать так

#define DO_JOIN(FOO, BAR) DO_JOIN1(FOO, BAR)
#define DO_JOIN1(FOO, BAR) FOO##BAR

вместо

#define DO_JOIN(FOO, BAR) FOO##BAR

A: Потому что препроцессор отработает конкатенацию не так, как того ожидал программист, в том случае, если в качестве аргумента(-ов) мы передадим в макрос DO_JOIN другой макрос:

#include <iostream>

#define DO_JOIN(FOO, BAR) DO_JOIN1(FOO, BAR)
#define DO_JOIN1(FOO, BAR) FOO##BAR

#define MY_MACRO 5

int main()
{
 std::cout << DO_JOIN(1, MY_MACRO) << '\n';
}

Output:

15

#include <iostream>

#define DO_JOIN(FOO, BAR) FOO##BAR

#define MY_MACRO 5

int main()
{
 std::cout << DO_JOIN(1, MY_MACRO) << '\n';
}

error: unable to find numeric literal operator 'operator"" MY_MACRO'

Получается, макрос MY_MACRO не успел раскрыться, в результате чего была попытка провести конкатенацию с 1 и MY_MACRO, что, разумеется, привело к ошибке.

ISO/IEC 14882:2011

16.3.1 Argument substitution [cpp.subst]

1 After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.

44. Что попадёт в stdout в результате выполнения след. кода?

#include <iostream>
#include <stdexcept>

struct Foo
{
 Foo() { std::cout << "Foo::Foo()" << std::endl; }
 ~Foo () { std::cout << "Foo::~Foo()" << std::endl; }
};

void foo()
{
 Foo bar;
 throw std::runtime_error("Error");
}

int main ()
{
 foo();
}

A: Зависит от реализации.

// 1

Foo::Foo()

// 2

Foo::Foo() Foo::~Foo()

ISO/IEC 14882:2011

15.3 Handling an exception [except.handle]

9 If no matching handler is found, the function std::terminate() is called; whether or not the stack is unwound before this call to std::terminate() is implementation-defined (15.5.1).

Например, в документации к gcc сказано, что раскрутки стека не произойдёт:

The stack is not unwound before std::terminate is called.

Сборник выкладывается на бесплатной основе, желающим помочь каким угодно образом буду безумно признателен и благодарен. По любым вопросам, рекомендациям и пожеланиям Вы можете обращаться на мой электронный ящик nikita.trophimov@gmail.com. Буду рад услышать абсолютно любое мнение по данному поводу.

В книге ещё много чего можно и даже нужно дорабатывать. Обещаю, что если сообщество проявит хоть какой-то интерес к данному проекту, я продолжу развивать его согласно Вашим предпочтениям и пожеланиям, дополнять новыми задачками и исправлять ошибки. У меня есть много мыслей по поводу улучшения «C and C++ Dark Corners» — среди них добавление тематических разделов, workaround'ы для различных компиляторов и т.д. Главное, чтобы этот интерес был не только с моей стороны.

Жду Ваших положительных и отрицательных отзывов, рекомендаций и, конечно, новых задачек.

Внимательно ознакомьтесь с тем, что указано во «введении», и приятного чтения!

Ответ на: комментарий от stevejobs

Про тексты я на лоре как минимум в этом году не писал. Вы меня с кем-то путаете.

anonymous
()
Ответ на: комментарий от stevejobs

не протягивай мне больше, пожайлуйста, свои влажные фантазии, не хотелось бы зашквариться

Прокукарекался.

платформа, которая выполняется прямо на процессоре - это жава, а ты даже не чииал стандарт, если не понимаешь как работает переносимость.

А, дак я говорю с жава-питушком. Похвально.

Не протягивай мне больше, пожайлуйста, свои влажные фантазии, не хотелось бы зашквариться.

Глупые мартышки научились рисовать на айпаде, скоро так же вслепую без стандарта научатся тыкать кнопки в конпеляторе, и всех скиллованных на бездумных кулфичах индусов выбросят в безвоздушное пространство.

Чёт уже 30лет прошло, а твой прогноз не сбылся. На айпаде вон за месяц научились, а тут надо 60лет?

Ктож твою «исполняющуся на процессоре» жаву писать-то будет, неужели ты, питушок?

А нормальные чуваки будут кодить на нормальных языках со статическими проверками, тайпклассами, IDE итп, а ваше крестоговно отправится получать Премию Дарвина

Ну да, ты как илитный питушок будешь кодить за еду до скончания дней своих - мне это не интересно.

И да, повторю вопрос, шкварёнок, кто будет ваять и поддерживать твою жаву, неужели ты, питушок?

настоящие лорды достаточно зарабатывают, чтобы кодить под нормальные, неигрушечные, платформы, имеют жен и секретарш, и жрут борщ с любимой мамкой.

А т.е. ты уже съехал, глупый кусок шкварёнка. Дак вот - я так же клал на игрушечные и протухшие платформы.

Влажные мечты оставь при себе - жабапитушок всегда мечтает об илите, буду тотальным говном, который неосилил кресты.

всякие питушары, запершись в гаражах, втачиваются как перебрасывать указатели и бегать по граблям, благо сквозь стены гаража не слышно ничего. Путь твоего исправления рекомендую начать с того, чтобы научиться жаве или хацкелю, бросить кододроч и попросить начальнка пост младшего помощника третьего продукт-менеджера с конца, завести, наконец, женщину и помириться с матушкой.

Ты мне рассказал как ты осиливал плюсы и как ты мечтаешь о заводе «женщины», наконец-то? Ок, мне это не интересно.

Меня не интересует ни работа за еду, ни питушки вроде тебя. Я делаю то, что интересно мне - а не то, что модно и изимодно.

Я не бегаю по граблям, ибо разница между мной и тобой в способнастях, понимании, скилле и осиляторстве той же матчасти просто несоизмерима. Ты ничтожное говно.

Мне не интерснен твой шаблонный планктонятский выхлоп, мне не интересны твои кулстори, как ты осиливал плюсы, просил начальника, а тебя не брали. Как ты наслушался историй про жабу и пошел говнякать на ней, и всё мечтаешь о чем-то.

anonymous
()
Ответ на: комментарий от anonymous

Примеры чего?

Примеры диалектов с интересными фичами.

Ваяй говно плюсовое - солью в говно, посмотришь фичи, если ты про фичи.

Непонятно.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Непонятно.

Как раз понятно: шизофазия обыкновенная

anonymous
()
Ответ на: комментарий от DarkEld3r

Непонятно.

он предлагает тебе написать код на крестах, который продемонстрировал бы фичи крестов, а он его оценит скильным взглядом и расскажет о недостатках и нужности. Так-то! Всегда ваш кэп)

stevejobs ★★★★☆
()
Последнее исправление: stevejobs (всего исправлений: 1)
Ответ на: комментарий от NikitaTrophimov

Писать технические тексты (да и вообще любые, кроме служебных записок и доносов) в Ворде - признак вопиющего непрофессионализма. На фоне этого читать, что же именно родил некомпетентный автор, уже как-то не хочется.

anonymous
()
Ответ на: комментарий от NikitaTrophimov

Такому коду место в test suite компилятора. Если ваша контора не пишет компилятор C++, то вас всех надо уволить на хер за то, что дрочите на рабочем месте в рабочее время.

anonymous
()
Ответ на: комментарий от asaw

Справедливости ради - работа сеньера в том и заключается, чтобы с первого взгляда на джуниорский говнокод понять, в чем там проблема, и объяснить лярве, как это надо переписать. А для этого надо знать про все залупы языка.

anonymous
()
Ответ на: комментарий от anonymous

Популярность функциональных языков, ИМХО, движется не в последнюю очередь теми, кто не осилил нормальный дизайн на объектно-ориентированных.

ФП и ООП ортогональны. На ФП дрочат те, кто не осилил правильных методов дизайна императивного кода, и правильных механизмов абстракции.

anonymous
()
Ответ на: комментарий от AIv

возможностью писать 1 строку там, где в ООП стиле пришлось бы лепить 10

Что, APL уже стал самым популярным языком?

anonymous
()
Ответ на: комментарий от anonymous

Обращаю внимание мебели на этого пизденыша - срочно банить блядино отродье по IP!

anonymous
()
Ответ на: комментарий от anonymous

Пизденышь, твоя мама - блядь и наркоманка. Пойди и передай ей это.

anonymous
()
Ответ на: комментарий от DarkEld3r

Примеры диалектов с интересными фичами.

Единственная действительно важная и нужная для оптимизаций фича, отсутствующая в C99 (гнутое расширение) - это computed goto.

anonymous
()
Ответ на: комментарий от anonymous

Единственная действительно важная и нужная для оптимизаций фича, отсутствующая в C99 (гнутое расширение) - это computed goto.

Ты другой анонмус или тот же?

Вопрос задавал в надежде, что что-то новое («вменяемые диалекты») узнаю. Про «computed goto» в курсе.

DarkEld3r ★★★★★
()
Ответ на: комментарий от vromanov

Хорошй программист пишет такой код, который не вызывает подобных вопросов. При обнаружении такого кода - переписать.

Так ведь не ознакомившись с предметом в нюансах, ты не сможешь обнаружить такой код. Просто потому, что ни в чём его не заподозришь. Поэтому плюсовиков надо заставлять учить сабжевую книгу наизусть и проверять на её знание при приёме на работу, а так же на переаттестациях каждые три года.

anonymous
()
Ответ на: комментарий от anonymous

Так ведь не ознакомившись с предметом в нюансах, ты не сможешь обнаружить такой код. Просто потому, что ни в чём его не заподозришь.

Он по-твоему имбецил что ли?

asaw ★★★★★
()
Ответ на: комментарий от DarkEld3r

Примеры диалектов с интересными фичами.

gnuc.

Непонятно.

Ваяй на плюсах то, что потвоему я не запилю на сишке «фичаста» - я запилю фичасто.

anonymous
()
Ответ на: комментарий от stevejobs

Ты уже зашкварился, шакварёныш - нахрен ты продолжаешь?

anonymous
()
Ответ на: комментарий от anonymous

gnuc.

Правильно понимаю, что подразумеваются расширения GCC?

Ваяй на плюсах то, что потвоему я не запилю на сишке «фичаста» - я запилю фичасто.

Ну так меряться мне не интересно.

Но разве тот же «computed goto» для с++ как расширение не доступен?

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Правильно понимаю, что подразумеваются расширения GCC?

Да.

Но разве тот же «computed goto» для с++ как расширение не доступен?

Мне-то какое дело? Конечно ты можешь запилить 30-40% фичей «на плюсах», но зачем? Форфан? Плюсами это не станет.

anonymous
()
Ответ на: комментарий от stevejobs

Давай свой модуль для русского интаксиса и грамматики в латехе. TITS OR GTFO.

не надо путать мокрое и соленое- для этого есть spellchecker

Олсо, начерта вы используете этот LaTeX? Почему не кошерный TeX?

Потому, что это удобно.

AIv ★★★★★
()
Ответ на: комментарий от asaw

Он по-твоему имбецил что ли?

Ну от чего же сразу имбецил? Может быть просто человек, далёкий от практики разработки ПО. Ведь мир состоит не из одних только программистов, встречаются среди людей и водители автобусов, продавцы картошки и даже милиционеры. И все они представляют себе разработку ПО примерно так же как и он. Мы же не считаем их за это имбецилами, правильно?

anonymous
()
Ответ на: комментарий от asaw

Да, а может ты просто сам имбецил? Тогда всё встаёт на свои места. Иначе какой тебе смысл врываться в тред и сводить дискуссию к безосновательным попыткам приписать анонимусу, то о чём и речи-то не шло?

Мне кажется, что своими неаргументированными выпадами ты просто пытаешься спровоцировать нездоровую дискуссию в этом треде. Я на тебя модераторам пожалуюсь.

anonymous
()
Ответ на: комментарий от anonymous

Анонимус, неаргументированно рассуждать про синьоров стал ты. Я, конечно, понимаю, что далекий от инженерии человек может представлять себе всякую ерунду об этой профессии, но не надо же так толсто троллить!

asaw ★★★★★
()
Ответ на: комментарий от anonymous

Конечно ты можешь запилить 30-40% фичей «на плюсах», но зачем? Форфан? Плюсами это не станет.

Зачем - чтобы пользоваться остальными вещами, которые язык предоставляет.

Точно так же можно сказать, что ты не на сишке пишешь, а на «расширениях». Только по факту всем пофиг, если задача решается.

DarkEld3r ★★★★★
()

Автору спасибо за труд. Подборка очень целостная, меткая и полезная на практике.

Ну а насчет неадекватной реакции местных, отсутствия интереса по существу, скатывания темы в «С++ — быдлоязык» и т.п. — я Вас, коллега, призываю не расстраиваться и не воспринимать близко к сердцу. Просто месседж немного не по адресу. Раздел «Development» ЛОРа уже давно не является сообществом профессиональных разработчиков (и я даже не знаю, был ли он таким когда-то). Профессионалы (то есть те, кто владеет технологиями на высоком уровне и зарабатывает на жизнь разработкой ПО) здесь в подавляющем меньшинстве и, я бы сказал, на птичьих правах. Абсолютное большинство — это тролли, диванные теоретики, доморощенные гуру, форумные трепачи, лисперы, хаскелисты и прочие неадекватные адепты маргинальщины, фрики, школьники, петросяны, IT-хипстеры, IT-эзотерики, IT-шарлатаны, IT-небыдло и так далее. Здесь в воздухе густо пахнет дилетантством и юношеским нонконформизмом. Здесь модно поливать помоями все практические, промышленные технологии и подходы (к которым, несомненно, относится и С++). Этим и объясняется происходящее. Странно еще, что не набежали советчики бросить С++ и изучать лисп, хаскель, Smalltalk, Brainfuck, Agda2 и Coq.

В свое время у Макскома (основателя ЛОРа) была идея превратить ЛОР в эдакий «русский StackOverflow». Увы, надежды не оправдались, а ЛОР как сообщество разработчиков предельно маргинализировался. О Хабре я тоже невысокого мнения, которое стало еще ниже после того, как они зарубили такую качественную публикацию.

Поэтому мне кажется, что Ваша аудитория в первую очередь англоязычная. Желаю автору таки осилить верстку в LaTeX, сделать перевод на английский и слайды в Beamer, с которыми выступить на какой-нибудь тематической конференции. Удачи!

anonymous
()
Ответ на: комментарий от DarkEld3r

Зачем - чтобы пользоваться остальными вещами, которые язык предоставляет.

Какими? Назови самые нужные. Никакими фичами кресты не обладают - всё из нужного есть в gnuc. А то, что не нужно - несёт больше вреда, чем пользы.

Точно так же можно сказать, что ты не на сишке пишешь, а на «расширениях».

Я пишу на gnuc. Что ты этим хотел сказать?

Только по факту всем пофиг, если задача решается.

Только вот про своей поддонный мир мне не надо рассказывать. Если тебе и кому-то пофиг - это говорит лишь о ваших примитивных взглядах на мир. Либо вы обитаете в областях, где можно ваять любое говно.

anonymous
()
Ответ на: комментарий от anonymous

В англоязычном мире все эти штуки давно обсосаны. Ничего нового и интересного в статье не вижу. Полезна она была бы как раз русскоязычным новичкам.

forCe
()
Ответ на: комментарий от forCe

Полезна она была бы как раз русскоязычным новичкам.

Причём только тем из них, кому мамка не даёт денег на платные книги.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.