Сказка про собес наоборот

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

Вы помните свое первое собеседование? Я свое помню отлично, преподаватель дольше обычного гонял мою плис в симуляторе Keil-C, придирался к любой мелочи, докапывался до каждой запятой в коде прошивки. А потом начал гонять по алгоритмам трассировки печатных плат, которые мы должны были проходить только в следующем семестре. Я уже мысленно готовился на допсу, видно же что валит, как и предыдущих двух одногруппников. Но в конце сдачи лабы по проектированию мк преподаватель спросил хочу ли я делать "железное железо для железной дороги?" (с). Студенту второго курса ИТМО, которого кормили родители, и подрабатывавшего разгрузкой вагонов ночами, это было сродни офферу в гугль. С тех пор я много раз побывал с обеих сторон стола, и в качестве испытуемого, и как придирчивый лид (отнюдь этим не горжусь, но и не стыжусь), и как группа поддержки у коллег из соседних отделов. Всегда хотелось надеяться, что наши собеседования - это не таинственные квесты, где каждая задача - это каст сложного заклятия, а ошибки не выкидывают с данжа.

Но сначала сказка о том, как к нам попал Миша: однажды в славном городе S погромист-джедай по имени Михаил отправился на собеседование в компанию "Кодозавры". Он был уверен, что знает все, и даже изучил новейшие фреймворки "NoScript" и "Unreal С--". Как он думал, ничто не может его остановить. Когда Миша пришел на собеседование, его встретил HR-менеджер по имени Анна. Она с улыбкой спросила: "Расскажите, как вы бы пояснили своей бабушке, что такое мьютекс?". Ага, прям с порога наш доблестный HR накинул Мишке затравочный вопрос. Ну а чё, это ж сказка, герои на белых самокатах и все такое...

Почему вы выбрали нашу компанию?
Почему вы выбрали нашу компанию?

Так что пусть наша Аня шарит в примитивах синхронизации. Мишка ушами не хлопал, улыбнулся и начал рассказывать про асинхронные задачи, колбэки и атомики. Любая бабушка будет гордиться таким внуком, подумал он. Затем Михаила пргиласили в избу, Анечка упорхнула на кухню варить кофф, не в силах осознать полученную информацию. Ой, нет - она пошла дописывать тестовое задание для новичков. Впрочем неважно, это же сказка - нашего героя провели по офису, показали крепостных на полях румяных девопсов (блин, некрасиво получилось) и направили в переговорку, куда чуть позже подошел лид отдела, в который и хотел попасть тогда еще юный наш герой. Не давая никому в комнате расслабиться, он сразу выдал стандартную загадку: "Как вы перевернете строку задом наперед без использования дополнительной памяти?", и уселся напротив кандидата писать код в своем смак-буке, кинув на себя спелл невидимости, чтобы не мешать Мише думать. Заклинание невидимости не прошло проверку на интеллект, но богатырь не подал виду и попробовал решить задачу в уме, ведь листочка тоже не дали. В итоге в голове у него произошел целый карнавал символов и индексов, в конце концов, он пришел к решению и объявил его "Въ развращеніи словесъ, начнити отъ крещениа и двигатисѧ ко началу, мѣнити буквы, въ обратномъ порядкѣ". Сказанного хватило, чтобы в переговорку телепортнулся босс данжа, т.е. тех лид студии и начал кидать в Мишу файрболы, ой... задавать скользкие и каверзные вопросы. Мишаня отбивался как мог, не путался в ответах, в минуты передышек провел пару атак световыми мечами и давил противника вопросами о техстеке, билдферме и процессе ревью. Наконец мана у босса в кружке опустилась до критического уровня, на последнем глотке тех лид студии "Кодозавры" дал задание переписать пульт управления ракетой на NoScript. Для камлания оставили смак-бук, час времени и какую-то матерь, призыв духов Гугола и Яру особых успехов не принес, зато дважды являлась Аннафея и предлагала испить живой воды, вода правда была коричневого цвета, и отчего-то пахла кофем, который не Нескафе, но Мишаня в фей не верил и изгонял искусительницу вопросами про спинлоки, после чего Аннафея таяла легкой дымкой.

Звезды сошлись, код смешался с загадками, магия ctrl+shift+B позволила увидеть три заветных слова: "Скомпилилось без ошибок!". Из данжа Михаил выходил победителем, апнул уровень харизмы и поднял скилл уверенности в своих силах. Босс как обычно в досадной злобе буркнул свое финальное проклятие - "Мы перезвоним вам через неделю", и не перезвонил. И отлично, потому что, тогда бы Мишаня не получил оффер от Gaijin, где и начал свое приключение в мире 3d-движков, которые выжимают 60фпс на дохлом железе при сотнях активных объектов на сцене и пром плюсов в полный рост. Знакомо?

Мы вам перезвоним! Aга, после дождичка в полнолуние
Мы вам перезвоним! Aга, после дождичка в полнолуние

Уфф... про световые мечи наверное было лишнее. Сказочным путем Мишани мне довелось пройти всего один раз, видимо сказывались знакомства. Через некоторое время я сам стал проводить собеседования, подбирая людей в команду. Тайны из наши собственных вопросов я никогда не делал, они както сами копились в текстовом файле и даже какое-то время висели на сайте компании в разделе "Карьера" и любой мог их посмотреть. Да и вопросами то их назвать сложно, скорее разные интересные фрагменты кода, которые ведут себя не так как предполагалось, какие-то сложнее, какие-то проще.

Поначалу, участвуя в других собеседованиях, я копировал и сами вопросы и устоявшиеся модели поведения. Но потом это стало надоедать, смысла в том что бы сильно налегать на матчасть или алгоритмы, если человек не впишется в команду не сильно много, если через пару месяцев придется искать нового. А ведь это и мои пара месяцев, которые я потрачу на обучение, отниму это время от своих задач и команды. Я не говорю, что не надо спрашивать алгоритмы, наоборот надо. Пусть человек сам расскажет какие знает и детально расскажет про пару по своему выбору. Перепробовав разные форматы собеседований, от сказочных, хорошо что был мудрый тех лид, который после первого такого праздника вызвал к себе и сказал, чтобы такой фигни он больше не видел, так что этот шаг мы проскочили сразу. Почти. Макс сорян, тебе просто не повезло, я был молод и глуп. Потом были стандартные на 10-15 вопросов на листе в стиле блиц опроса с минутой на обдумывание и небольшого тестового на час здесь и сейчас.

Позже мы с командой пришли к довольно необычному процессу отбора, назовем его "собес наоборот" - именно после таких собесов мы получили несколько отличных спецов в отдел, которые бы не попали к нам по другому. Как потом оказалось, один ненавидел тестовые и просто уходил с собеса, когда ему предлагали их делать, при этом у него шикарный гитхаб (https://github.com/megai2/d912pxy) и человек в одиночку тянул нехилый пет-проект на 1к звездочек. Второй просто выгорел на прошлом месте работы, разочаровался в программировании и спотыкался на базовых вопросах по стандарту и стл, но раскрылся когда речь зашла о низкоуровневых оптимизациях на уровне асма, или надо было пореверсить алгоритм из асма и все подобное этому. Основной упор старались делать на тех людей, знаний которых не хватало в команде, даже если он не проходил по другим критериям. Не утверждаю, что это панацея, собес это в любом случае разговор двух сторон, которые пытаются продать себя друг другу. Как представитель компании, я продаю компанию человеку и смысла врать про пульт управления ракетой нет, если по выходу на пульте придется только перекрашивать кнопки и обновлять разметку. Хвала небесам, это не про игрострой, интересных задач тут навалом, успевай разгребать только.

А че, так можно было?
А че, так можно было?

Со стороны человека который идет в компанию, (как мне кажется) тоже особо нет смысла преувеличивать свое умение чинить пульты разных видов, с плюсиками или бемолями, квадратные или змеевидные. Или хвастаться починкой пультов от Когдазавра и Роботоноида версии 2.0. Все равно квадратный зеленый пульт мы чинить не умеем, или умеем, но еще не знаем как и вечером придется читать документацию. Как представитель себя любимого, я стараюсь получить побольше денег и (поменьше звиздеца в любых проявлениях) интересные для меня качество и количество задач.

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

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

Достаем TXT файлик, которому очень много лет
Достаем TXT файлик, которому очень много лет

Собственно это все, что я хотел рассказать. А теперь танцы, т.е. вопросы. Часть вопросов уже поднимались вот тут (https://habr.com/ru/articles/764514/ дичь от Феди, я смотрю многим пришлась по вкусу), надеюсь эти будут не хуже, могут быть дубли. Какие-то вопросы покажутся вам простыми, значит вы много пишите кода, и вообще человек хороший:

0х1...
Начну пожалуй с классического вопроса, что будет выведено в результате работы программы?

void sayHello() {
    std::cout << "Hello, World!\n";
}
void sayНello() {
    std::cout << "Goodbye, World!\n";
}
int main() {
    sayНello();
    return 0;
}
А что не так?

А надо скомпилить, тогда в консоль будет выведено "Goodbye, World!". Потому что вторая sayHello написана с использование юникода, который к сожалению не всегда палится. И она же вызывается (clang, latest)

sayHello():                           # @sayHello()
        push    rbp
        mov     rbp, rsp
        mov     rdi, qword ptr [rip + std::cout@GOTPCREL]
        lea     rsi, [rip + .L.str]
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@PLT
        pop     rbp
        ret
"_Z9sayНellov":                         # @"_Z9say\D0\9Dellov"
        push    rbp
        mov     rbp, rsp
        mov     rdi, qword ptr [rip + std::cout@GOTPCREL]
        lea     rsi, [rip + .L.str.1]
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@PLT
        pop     rbp
        ret

0х2...
А эта функция обладает удивительной способностью изменять реальность в зависимости от того, сколько ног у ее вызывающего.

int abs_legs(int my_legs) {
  if (my_legs < 0) {
    return -my_legs;
  }
}
А что не так?

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

int abs_legs(int my_legs) {
  if (my_legs < 0) {
    return -my_legs;
  }
  return my_legs;
}

0х3...
Эта функция не раскрывается полностью, оставляя свою магию за покровом компилятора. Где деньги, Зин?

int get_money(int index, const int *pockets) {
  int a = index + pockets[++index];
  // ...

  return a;
}
А что не так?

Компилятор может переставить порядок операции для index + pockets[++index], и это приведет к неоднозначному поведению с разными настройками оптимизации. Неупорядоченная или неопределенная последовательность операций приведет к сайд эффекту при работе с переменной index

int get_money(int index, const int *pockets) {
  ++index;
  int a = index + pockets[index];
  // ...
}

0х4...
Что будет выведено в bufMatrix?

void umcp_read_buffer_from_pipe() {
  char bufKernel[12];
  char bufMatrix[12];
  std::cin.width(12);
  std::cin >> bufKernel;
  std::cin >> bufMatrix;
}
А что не так?

В этом примере первое чтение не приведет к переполнению, и заполнит bufKernel усеченной строкой. Но второе чтение может переполнить bufMatrix, чтобы этого не произошло надо также вызвать std::cin.width(12); перед получением bufMatrix. Или воспользоваться безопасной работой через строки.

void umcp_read_buffer_from_pipe() {
  std::string bufKernel, bufMatrix;
  std::cin >> bufKernel >> bufMatrix;
}

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

std::string str_func();
void display_string(const char *);

void set_display_options() {
  const char *str = str_func().c_str();
  display_string(str);
}
А что не так?

Здесь std::string::c_str() вызывается для временного объекта std::string. Полученный указатель будет указывать на освобожденную, но возможно еще валидную, память после уничтожения объекта std::string в конце выражения присваивания. Правда не очень на это надейтесь, это приведет к неопределенному поведению при доступе этому указателя.

std::string str_func();
void display_string(const char *s);

void set_display_options() {
  std::string str = str_func();
  const char *cstr = str.c_str();
  display_string(cstr);
}

0х6...
Сколько раз за вечер бар будет закрыт?

void ьщкт() {
  int *bar = new int;
  std::shared_ptr<int> p1(bar);
  std::shared_ptr<int> p2(bar);
}
А что не так?

Здесь два несвязанных смарт указателя созданы на основе одного и того же базового значения. Когда время жизни переменная p2 закончится, она удалит ресурс, которым управляет. Об этом не узнает переменная p1, которая попробует выполнить удаление этого ресурса повторно, что-то да удалит в итоге. Но это уже совсем другая история.

void night() {
  std::shared_ptr<int> p1 = std::make_shared<int>();
  std::shared_ptr<int> p2(p1);
}

0х7...
Почему утром опасно ходить в bar?

void morning(const std::string &owner) {
  std::fstream bar(owner);
  if (!bar.is_open()) {
    // Handle error
    return;
  }

  bar << "customer";

  std::string str;
  bar >> str;
}
А что не так?

Потому что можно не выйти. Данные добавляются в конец файла, а затем считываются из того же файла. Однако, поскольку поскольку указатель в файле остался на прежнем месте в конце, то попытка прочитать данные из конца файла приведет к UB. В бар надо ходить вечером, то все "замечательно выходит" (с).

void evening(const std::string &owner) {
  std::fstream bar(owner);
  if (!bar.is_open()) {
    // Handle error
    return;
  }

  bar << "customer";

  std::string str;
  bar.seekg(0, std::ios::beg);
  bar >> str;
}

0х8...
Какое одно слово надо изменить, чтобы этот код заработал?

struct Foo {
  void *foo;
  struct foo *next;
};

static Foo foos;
static std::mutex m;
static std::condition_variable condition;

void consume_list_element(std::condition_variable &condition) {
  std::unique_lock<std::mutex> lk(m);

  if (foos.next == nullptr) {
    condition.wait(lk);
  }

  // Proceed when condition holds.
}
А что не так?

После того как переменная condition будет отпущена, надо проверить не забрал ли кто элемент пока мы переключали контекст.

  while (foos.next == nullptr) {
    condition.wait(lk);
  }

0х9...
Почему этот rand() работает как генератор случайных чисел от "Sony"? Здесь надо немного пояснить про этот мем, для генерации rand() в своих секретных ключах Сони, использовала секретную функцию которая из-за ошибки реализации возвращала одного и тоже число.

int main() {
  std::string id("ID");
  id += std::to_string(std::rand() % 10000);
  // ...
}
А что не так?

Полученное путем вызова функции rand() число предсказуемо и имеет ограниченную случайность. Чтобы избавиться от повторения сгенерированной последовательности в некоторых реализациях std::rand(), надо выбрать генератор, предоставляющий истинно неповторяющуюся последовательность. Например mtXXXX (https://dl.acm.org/doi/10.1145/272991.272995)

int main() {
  std::string id("ID");
  std::uniform_int_distribution<int> distribution(0, 10000);
  std::random_device rd;
  std::mt19937 engine(rd());
  id += std::to_string(distribution(engine));
  // ...
}

0хА...
Получит ли ведьмак монеты?

class Witcher {
  int i;

public:
  virtual int TakeCoins(int l) { return i += 100; }
};

int main() {
  Witcher *geralt = new Witcher();

  memset(geralt, 0, sizeof(Witcher));

  std::cout << geralt->TakeCoins(100);
  return 0;
}
А что не так?

Лучше не использовать memset() для работы с полиморфик типами, она потрет указатель на vtbl в классе, такой объект становится невалидным и попытка поработать с ним будут приводить к UB

class Witcher {
  int i = 0;

public:
  virtual int TakeCoins(int l) { return i += 100; }
};

int main() {
  Witcher *geralt = new Witcher();

  std::cout << geralt->TakeCoins(100);
  return 0;
}

0xB...
Станет ли бард пьяницей?

class Bard {
  int _beer;
  int _meal;

public:
  Bard(int meal) : _meal(meal), _beer(_meal - 1) {}
};
А что не так?

Cписок инициализации мемберов нарушен и сначала будет установлена переменная _beer, а затем будет инициализирована переменная _meal. Поскольку порядок объявления переменны не соответствует тому что заявлено в описании класса, попытка прочитать значение _meal приводит к тому, что оно имеем неопределенное значение. Скорее всего бард сопьется без закуси.

class Bard {
  int _beer;
  int _meal;

public:
  Bard(int beer) : _beer(beer), _meal(_beer + 1) {}
};

0хС...
В каком случае выстрел по ногам будет особенно результативным?

for (auto format = begin(formats), __end = end(formats); format != __end; ++format) {
  if (snd::CodecNamesEq(....)) {
    format.is_stereo = true;
    formats.push_back(stereo_format);
  }
}
А что не так?

Если в процессе push_back() произойдет реалокация formats, итератор format будет указывать уже на удаленную память, работа с которой вызовет кучу проблем.

0хD...
Этот код лучше попробовать собрать самому, чтобы понять админ ли вы? (https://onlinegdb.com/rMnPPq5et)

int main() {
    std::string access_level = "user";
    if (access_level.compare("user‮ ⁦// Check if admin⁩ ⁦")) {
        std::cout << "You are an admin.\n";
    }
    return 0;
}

0xE...
Иногда в с++ просто вхух и работает!

.∧_∧
( ・ω・。)つ━☆・*。
⊂  ノ    ・゜+.
しーJ   °。+ *´¨)
         .· ´¸.·*´¨) ¸.·*¨)
          (¸.·´ (¸.·'* ☆ Wizz, just works! ☆
Но чаще приходится долго отлаживать
Но чаще приходится долго отлаживать

0xF...
Ни один Мишаня в результате наших собесов не пострадал, Костя ответил на все вопросы и добавил в список пару новых. Особо отличившиеся получили предложение присоединиться к команде, просто умные люди шли дальше на собес в iOS/Android team. А Максим потом нашел себя в бекэнд команде красного банка.

Вот и сказочке конец, а кто дочитал, благодарю за внимание.

Источник: https://habr.com/ru/articles/765174/


Интересные статьи

Интересные статьи

Статей про собеседования много. Но в основном они о том, как достойно пройти интервью кандидату. Я провожу собеседования более 5 лет и хотела бы дать пару-тройку советов интервьюерам. Важно понимать, ...
Каждый начинающий девопс мечтает об этом — о долгожданном оффере. Но перед тем, как его получить, придется пройти ряд интересных квестов, и среди них — собеседование. Мы собрали круглый стол из спикер...
Ребята, всем привет!!!Выдалась у меня свободная минута и решил я собрать небольшой гайд на прохождение собеседования по направлению программиста 3D графики для GameDev ко...
По мотивамЧасть 0x00Часть 0x01Часть 0x02Ребятушки, наконец! После довольно длительного перерыва ко мне, в конце концов, пришло вдохновение написать последнюю, финальную и...
Привет, хабр!В прошлой статье меня знатно разбомбили в комментариях, где-то за дело, где-то я считаю, что нет. Так или иначе, я выжил, и у меня есть чем с вами поделиться >:)Напомню, ч...