Вы помните свое первое собеседование? Я свое помню отлично, преподаватель дольше обычного гонял мою плис в симуляторе Keil-C, придирался к любой мелочи, докапывался до каждой запятой в коде прошивки. А потом начал гонять по алгоритмам трассировки печатных плат, которые мы должны были проходить только в следующем семестре. Я уже мысленно готовился на допсу, видно же что валит, как и предыдущих двух одногруппников. Но в конце сдачи лабы по проектированию мк преподаватель спросил хочу ли я делать "железное железо для железной дороги?" (с). Студенту второго курса ИТМО, которого кормили родители, и подрабатывавшего разгрузкой вагонов ночами, это было сродни офферу в гугль. С тех пор я много раз побывал с обеих сторон стола, и в качестве испытуемого, и как придирчивый лид (отнюдь этим не горжусь, но и не стыжусь), и как группа поддержки у коллег из соседних отделов. Всегда хотелось надеяться, что наши собеседования - это не таинственные квесты, где каждая задача - это каст сложного заклятия, а ошибки не выкидывают с данжа.
Но сначала сказка о том, как к нам попал Миша: однажды в славном городе Панкт-Сетербург погромист-джедай по имени Михаил отправился на собеседование в компанию "Кодозавры". Он был уверен, что знает все, и даже изучил новейшие фреймворки "NoScript" и "Unreal С--". Как он думал, ничто не может его остановить. Когда Миша пришел на собеседование, его встретил HR-менеджер по имени Анна. Она с улыбкой спросила: "Расскажите, как вы бы пояснили своей бабушке, что такое мьютекс?". Ага, прям с порога наш доблестный HR накинул Мишке затравочный вопрос. Ну а чё, это ж сказка, герои на белых самокатах и все такое...
Так что пусть наша Аня шарит в примитивах синхронизации. Мишка ушами не хлопал, улыбнулся и начал рассказывать про асинхронные задачи, колбэки и атомики. Любая бабушка будет гордиться таким внуком, подумал он. Затем Михаила пригласили в избу, Анечка упорхнула на кухню варить кофф, не в силах осознать полученную информацию. Ой, нет - она пошла дописывать тестовое задание для новичков. Впрочем неважно, это же сказка - нашего героя провели по офису, показали крепостных на полях румяных девопсов (блин, некрасиво получилось) и направили в переговорку, куда чуть позже подошел лид отдела, в который и хотел попасть тогда еще юный наш герой. Не давая никому в комнате расслабиться, он сразу выдал стандартную загадку: "Как вы перевернете строку задом наперед без использования дополнительной памяти?", и уселся напротив кандидата писать код в своем смак-буке, кинув на себя спелл невидимости, чтобы не мешать Мише думать. Заклинание невидимости не прошло проверку на интеллект, но богатырь не подал виду и попробовал решить задачу в уме, ведь листочка тоже не дали. В итоге в голове у него произошел целый карнавал символов и индексов, в конце концов, он пришел к решению и объявил его "Въ развращеніи словесъ, начнити отъ крещениа и двигатисѧ ко началу, мѣнити буквы, въ обратномъ порядкѣ". Сказанного хватило, чтобы в переговорку телепортнулся босс данжа, т.е. тех лид студии и начал кидать в Мишу файрболы, ой... задавать скользкие и каверзные вопросы. Мишаня отбивался как мог, не путался в ответах, в минуты передышек провел пару атак световыми мечами и давил противника вопросами о техстеке, билдферме и процессе ревью. Наконец мана у босса в кружке опустилась до критического уровня, на последнем глотке тех лид студии "Кодозавры" дал задание переписать пульт управления ракетой на NoScript. Для камлания оставили смак-бук, час времени и какую-то матерь, призыв духов Гугола и Яру особых успехов не принес, зато дважды являлась Аннафея и предлагала испить живой воды, вода правда была коричневого цвета, и отчего-то пахла кофем, который не Нескафе, но Мишаня в фей не верил и изгонял искусительницу вопросами про спинлоки, после чего Аннафея таяла легкой дымкой.
Звезды сошлись, код смешался с загадками, магия ctrl+shift+B позволила увидеть три заветных слова: "Скомпилилось без ошибок!". Из данжа Михаил выходил победителем, апнул уровень харизмы и поднял скилл уверенности в своих силах. Босс как обычно в досадной злобе буркнул свое финальное проклятие - "Мы перезвоним вам через неделю".
И не перезвонил. И отлично, потому что, тогда бы Мишаня не получил оффер от Gaijin, где и начал свое приключение в мире 3d-движков, которые выжимают 60фпс на дохлом железе при сотнях активных объектов на сцене и пром плюсов в полный рост. Знакомо?
Уфф... про световые мечи наверное было лишнее. Сказочным путем Мишани мне довелось пройти всего один раз, видимо сказывались знакомства. Через некоторое время я сам стал проводить собеседования, подбирая людей в команду. Тайны из наших собственных вопросов я никогда не делал, они както сами копились в текстовом файле и даже какое-то время висели на сайте компании в разделе "Карьера" и любой мог их посмотреть. Да и вопросами то их назвать сложно, скорее разные интересные фрагменты кода, которые ведут себя не так как предполагалось, какие-то сложнее, какие-то проще.
Поначалу, участвуя в других собеседованиях, я копировал и сами вопросы и устоявшиеся модели поведения. Но потом это стало надоедать, смысла в том что бы сильно налегать на матчасть или алгоритмы, если человек не впишется в команду не сильно много, если через пару месяцев придется искать нового. А ведь это и мои пара месяцев, которые я потрачу на обучение, отниму это время от своих задач и команды. Я не говорю, что не надо спрашивать алгоритмы, наоборот надо. Пусть человек сам расскажет какие знает и детально расскажет про пару по своему выбору. Перепробовав разные форматы собеседований, от сказочных, хорошо что был мудрый тех лид, который после первого такого праздника вызвал к себе и сказал, чтобы такой фигни он больше не видел, так что этот шаг мы проскочили сразу. Почти. Макс сорян, тебе просто не повезло, я был молод и глуп. Потом были стандартные на 10-15 вопросов на листе в стиле блиц опроса с минутой на обдумывание и небольшого тестового на час здесь и сейчас.
Позже мы с командой пришли к довольно необычному процессу отбора, назовем его "собес наоборот" - именно после таких собесов мы получили несколько отличных спецов в отдел, которые бы не попали к нам по другому. Как потом оказалось, один ненавидел тестовые и просто уходил с собеса, когда ему предлагали их делать, при этом у него шикарный гитхаб (https://github.com/megai2/d912pxy) и человек в одиночку тянул нехилый пет-проект на 1к звездочек. Второй просто выгорел на прошлом месте работы, разочаровался в программировании и спотыкался на базовых вопросах по стандарту и стл, но раскрылся когда речь зашла о низкоуровневых оптимизациях на уровне асма, или надо было пореверсить алгоритм из асма и все подобное этому. Основной упор старались делать на тех людей, знаний которых не хватало в команде, даже если он не проходил по другим критериям. Не утверждаю, что это панацея, собес это в любом случае разговор двух сторон, которые пытаются продать себя друг другу. Как представитель компании, я продаю компанию человеку и смысла врать про пульт управления ракетой нет, если по выходу на пульте придется только перекрашивать кнопки и обновлять разметку. Хвала небесам, это не про игрострой, интересных задач тут навалом, успевай разгребать только.
Со стороны человека который идет в компанию, (как мне кажется) тоже особо нет смысла преувеличивать свое умение чинить пульты разных видов, с плюсиками или бемолями, квадратные или змеевидные. Или хвастаться починкой пультов от Когдазавра и Роботоноида версии 2.0. Все равно квадратный зеленый пульт мы чинить не умеем, или умеем, но еще не знаем как и вечером придется читать документацию. Как представитель себя любимого, я стараюсь получить побольше денег и (поменьше звиздеца в любых проявлениях) интересные для меня качество и количество задач.
В чем идея "собеса наоборот", все тот же список вопросов, будь он неладен, только мы просили выбрать их по желанию и задать нам, сами отвечали на вопросы, иногда сознательно опуская некоторые моменты, иногда внося неточности, а наш будущий коллега, подмечал или не подмечал это. Добавлял и поправлял, или не поправлял наши рассказы, в большинстве случаев человека вовлекали в разговор.
На тех собесах я не помню чтобы мы писали сложный код, чаще вообще не писали, разве что были небольшие пояснения на бумаге. Ломалась сама атмосфера собеседования как экзамена, и вот уже кандидат пытается подловить нас на ответах, задает вопросы не из списка, несколько раз не могли потом успокоить по полчаса - вопросы сыпались один за другим, приходилось кастовать смак-бук и тестовое. Обращали внимание на умение человека ясно и внятно изложить свои мысли, проговорить идею, задать вопрос, потому что матчасть можно подтянуть, не сейчас так через месяц-другой. Скилл в починке скругленных серебристых тачскринов тоже со временем наработается, если человек не особо ленив. А вот культуру умения вести разговор, обмена идеями и обсуждения технических вопросов в ревью, не обидив при этом коллег и не вернув его 120 раз на доработку, осилить (а главное принять) намного сложнее.
Собственно это все, что я хотел рассказать. А теперь танцы, т.е. вопросы. Часть вопросов уже поднимались вот тут (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 evening() {
int *bar = new int;
std::shared_ptr<int> p1(bar);
std::shared_ptr<int> p2(bar);
}
А что не так?
Закроют бар один раз, потом попробуют снести город, в котором он стоит. Здесь два несвязанных смарт указателя созданы на основе одного и того же базового значения. Когда время жизни переменной p2 закончится, она удалит ресурс, которым управляет. Об этом не узнает переменная p1, которая попробует выполнить удаление этого ресурса повторно, что-то да удалит в итоге. Но это уже совсем другая история.
void evening() {
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;
void consume_list_element(const std::condition_variable &condition) {
std::unique_lock<std::mutex> lk(m);
if (foos.next == nullptr) {
condition.wait(lk);
}
// Proceed when condition holds.
}
А что не так?
После того как будет получен сигнал для condition_variable condition, нужно повторно проверить не забрал ли кто элемент пока мы переключали контекст или поток находился в ожидании. Если этого не сделать, возможна работа с указателем на null
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 coins) { return i += coins; }
};
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 coins) { return i += coins; }
};
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. А Максим потом нашел себя в бекэнд команде красного банка.
Вот и сказочке конец, а кто дочитал, благодарю за внимание.
Комментарии (76)
SpiderEkb
04.10.2023 03:48+8Вопросы, конечно, хорошие в целом. Но, на мой взгляд, слишком частные. Лично мне всегда интересно как человек будет подходить к решению той или иной конкретной задачи. В целом.
Не нужно конкретного кода. Просто общую канву решения. Мы же не кодировщика берем который будет переводить псевдокод из ТЗ на конкретный язык, а разработчика, которому будет сказано - "на входе у тебя будет это, на выходе должен получить то, граничные условия такие-то, сценарий использования такой-то". И задача разработчика выбрать и реализовать наиболее эффективный алгоритм реализации.
Это не тестовое задание ни в коем случае. Это понять как человек думает, его способность к алгоритмизации.
eao197
04.10.2023 03:48+13Есть люди (вроде меня), которые получив какую-то задачу на первое время впадают в состояние абсолютного тумана в голове. Нет никаких внятных мыслей, в голове роятся разрозненные отрывки из обрывков. Вроде "так, если здесь вот это, то..." и потом обрывается, а вместо этого приходит "а вот если здесь вот так, то..." и потом опять обрывается, чтобы смениться следующим обрывком совсем другой мысли. И так длится какое-то время (в зависимости от задачи от десятка минут до нескольких дней, а то и недель). Чтобы потом внезапно (действительно внезапно, это не фигура речи) туман вдруг отступил и проявились очертания вполне себе четкого решения и вот уже дальше все идет "как по писанному".
Так вот проблема в том, что если в момент "тумана в голове" у меня кто-то будет выпытывать логические построения, то выглядеть я буду, мягко говоря, идиотом. Пык-мык и "не знаю", "не думаю", "не представляю" + рисование ничего не значащих каракулей на бумаге.
В общем, ваш интерес к тому, как человек подходит к решению с некоторыми персонажами не будет удовлетворен. Вы, скорее, разочаруетесь и придете к выводу, что перед вами какой-то самозванец, который на прошлых местах работы выезжал за счет чужого труда, а сам ничего придумать не может.
SpiderEkb
04.10.2023 03:48+1Есть люди (вроде меня), которые получив какую-то задачу на первое время впадают в состояние абсолютного тумана в голове. Нет никаких внятных мыслей, в голове роятся разрозненные отрывки из обрывков. Вроде "так, если здесь вот это, то..." и потом обрывается, а вместо этого приходит "а вот если здесь вот так, то..." и потом опять обрывается, чтобы смениться следующим обрывком совсем другой мысли.
Ну вот это для меня и интересно. Мне не нужен "правильный ответ". Мне нужно понять как человек будет думать. Потому что мне потом с ним работать и хочется примерно представлять ход мыслей "товарища по команде".
Лично для меня собес это не "ответил правильно на семь вопросов из десяти", а понять человека. На что он в принципе способен, насколько с ним можно что-то обсуждать.
И подискутировать "а если тут вот так, то что?" в процессе считаю совершенно нормальным.
Сразу провести грань между "я человек маленький, сюда за зарплатой хожу, что скажете, то и делаю" и "я считаю что надо вот так потому что..."
Понятно, что это не касается позиций джунов с минимальным опытом. Только людей с хорошим опытом разработки.
eao197
04.10.2023 03:48+2Мне нужно понять как человек будет думать.
Так в том-то и дело, что с некоторыми людьми вы этого не поймете. Лучший способ взаимодействовать с ними -- это дать задачу и позволить скрыться с глаз на какое-то время. Чтобы он потом вернулся к вам с решением. И уже решение можно будет обсудить. Именно решение. Пути его поиска вы не увидите. Потому что в явном виде его нет (пути этого самого): сначала ничего нет, туман и путаница, а потом "Хоп!" и вот же оно же ж.
Собственно, если вы попытаетесь попробовать проследить как человек думает на собеседовании, то вы ничего не увидите. Человек будет молчать, это молчание будет длится все дольше и дольше и от затянувшейся паузы будет не по себе всем сторонам.
SpiderEkb
04.10.2023 03:48Вопрос в том что мне не нужно решение. Мне нужно понять как человек к нему приходит. Ну мало у нас задач, которые сразу можно решить. А "олимпиадные" задачки оторваны от жизни.
Для меня может оказаться достаточно если человек сразу задаст ряд правильных уточняющих вопросов.
Да и задачки не настолько сложные алгоритмически чтобы голову ломать. Скорее на выбор правильного архитектурного подхода. И даже если человек скажет "можно вот так, а можно вот этак, зависит от ..."
Ну и человек, который сядет и месяц будет ждать озарения точно не нужен. Извините уж. Мне интересен человек, у которого есть готовые паттерны для решения задач, являющихся для нас типовыми.
Ну как пример.
Нужно из БД сделать выборку по определенным условиям. В нее попадет порядка 10 000 000 записей. Каждую нужно определенным образом обработать (обработка одной записи никак не связана с обработкой остальных). Скорость обработки порядка 1 000 записей в секунду. Нужно уложиться в полчаса. Как?
Понятно что "человек с паттернами" сразу скажет - "распараллеливаем обработку на несколько потоков". А дальше уже можно будет обсудить как он это видит. Как он планирует реализовать, например, распределение обрабатываемых записей по потокам так, чтобы загрузка была равномерной. Ну и т.д. и т.п. И все это без кода. Только общая схема.
Т.е. вот такого плана беседа, а не "сортировка пузырьком" и не "разворот строки или списка".
eao197
04.10.2023 03:48+15Вопрос в том что мне не нужно решение.
Вопрос, видимо, в том, что вы не можете меня понять. Я вам говорю о том, что есть люди, которым вы задаете задачу вроде:
Нужно из БД сделать выборку по определенным условиям. В нее попадет порядка 10 000 000 записей. Каждую нужно определенным образом обработать (обработка одной записи никак не связана с обработкой остальных). Скорость обработки порядка 1 000 записей в секунду. Нужно уложиться в полчаса. Как?
И ожидаете, что человек заикнется про "распараллеливание на несколько потоков" и дальше вы с ним будете обсуждать про распределение записей и т.д.
А столкнетесь с тем, что человек вообще замолчит на полчаса и займется рисованием каракулей на бумаге. Чтобы через полчаса предложить вам один или два варианта, более-менее проработанных, в которых уже будут учтены какие-то граничные условия и возможные проблемы. И вот это вот вы с ним и сможете обсудить. Но не сразу, а через какое-то время, в течении которого человек и для вас, и для себя, будет выглядеть идиотом.
Собственно, проблема в том, что такого длительного ожидания на собеседовании никто не допустит.
Ну и речь не о том, что ваш подход не правильный (или что он самый правильный). Речь о том, что есть условия, когда вы не получите того, чего желаете. Но это ничего не скажет о пригодности соискателя.
SpiderEkb
04.10.2023 03:48А столкнетесь с тем, что человек вообще замолчит на полчаса и займется рисованием каракулей на бумаге.
Значит это человек не того уровня, который мне нужен.
Чтобы через полчаса предложить вам один или два варианта, более-менее проработанных, в которых уже будут учтены какие-то граничные условия и возможные проблемы.
Мне не нужно сразу проработку. Это уже предмет дальнейшей дискуссии. Если он не может сразу выбрать правильное направление, ну что ж...
Вы поймите - есть типовые задачи и есть типовые их решения. И есть сроки. Если человек на типовую задачу ждет "озарения" - это не то что тут нужно.
Естественно, если человек приходит на джуна - с него спрос другой совсем. Но если он позиционирует себя как сеньор или хотя бы мидл - у него должен быть багаж "паттернов". И задача выяснить насколько его паттерны совпадают с нашими.
Заметьте, я не буду гонять его задачками как в этой статье. Просто потому что в этой ветке вряд ли кто сможет ответить на вопрос что выведется на экран после вот такого:
dcl-ds dsSample qualified; fld1 varchar(50); fld2 char(50); end-ds; ... clear dsSample; dsply %char(%len(dsSample.fld1));
Более того, вряд ли кто сможет сказать почему результат будет 16448 :-)
Речь о том, что есть условия, когда вы не получите того, чего желаете. Но это ничего не скажет о пригодности соискателя.
А что скажет? Умение угадывать как будет оптимизирован тот или иной кусок кода тем или иным компилятором при тех или иных установках? Умение написать сортировку пузырьком или разворот списка? 100500 вызубренных задачек с литкода? Так нам в нашей реальной жизни все это малопригодно.
От кандидата никто не ждет что он сразу начнет "гнать код в пром". Больше - первые три месяца будет обучение. Платформе, языку - они у нас специфические, готовых разработчиков на всю страну сотни три и все так или иначе трудоустроены и знают друг друга или напрямую или через общих знакомых.
Вообще, собес у нас достаточно неформальный - человек рассказывает чем занимался он, какой есть опыт. Мы рассказываем чем занимаемся мы. Вполне возможно, что его что-то у нас не устроит - ну что ж...
Если я буду что-то спрашивать типа того, что привел выше, то буду стараться выбирать ту тему, в которой у человека уже есть какой-то опыт (с его слов).
И что он ответит не будет решающим критерием "берем-не берем". Скорее просто понять общий уровень. А брать-не брать это уже решается голосованием потом, в команде. По общему впечатлению.
Как ни странно, но это работает.
eao197
04.10.2023 03:48+1Значит это человек не того уровня, который мне нужен.
Очевидно так.
Очевидно, что вы меня не услышали.
SpiderEkb
04.10.2023 03:48-1Ну вы тоже не хотите слышать.
Я в последнем посте специально отметил, что задавать вопросы буду стараться в рамках тех компетенций, которые человек сам отметил.
Я не думаю, что если человек скажет что у него есть опыт работы с многопоточкой, а я спрошу как он организовывал обмен данными между потоками и какие при этом возникали сложности, он
вообще замолчит на полчаса и займется рисованием каракулей на бумаге
ибо если так, то это точно не тот кто нужен.
Я давно живу и много чего видел. В том числе, умных и талантливых людей (реально умных и реально талантливых), но совершенно не способных к коммерческой разработке. В академической среде они показывали себя с наилучшей стороны. Но когда нужно сделать задачу в заданном объеме и в заданный срок - все... "Озарение не снизошло" вовремя и сроки все сорваны. Остальным приходится все бросать и разгребать.
dalerank Автор
04.10.2023 03:48тут скорее надо уметь работать с rockstar разработчиками, и далеко не всегда для них есть стек задач, а на рутине они быстро гаснут.
eao197
04.10.2023 03:48+1Ну вы тоже не хотите слышать.
Это не так.
Я не думаю, что если человек скажет что у него есть опыт работы с многопоточкой, а я спрошу как он организовывал обмен данными между потоками и какие при этом возникали сложности, он
Вообще-то я вам указал на другое, а именно:
Лично мне всегда интересно как человек будет подходить к решению той или иной конкретной задачи.
И, тем более, с конкретным примером:
Нужно из БД сделать выборку по определенным условиям. В нее попадет порядка 10 000 000 записей. Каждую нужно определенным образом обработать (обработка одной записи никак не связана с обработкой остальных). Скорость обработки порядка 1 000 записей в секунду. Нужно уложиться в полчаса. Как?
Т.е. вы даете человеку общего вида задачу и далее хотите услышать его рассуждения о том, как бы он эту задачу решал.
Так вот, я вам о том, что есть люди, для которых "рассуждения в слух" не есть не то, что привычно, но это даже и ненормально.
Вы просто отфутболите таких людей. И, судя по тому, что вы пишете, вас это устраивает. Ну и нет проблем, хозяин барин. Правда, здесь мне было бы интересно узнать, а вы владеете бизнесом в который набираете персонал?
Но когда нужно сделать задачу в заданном объеме и в заданный срок - все... "Озарение не снизошло" вовремя и сроки все сорваны.
Речь не об этом. "Озарение снизошло" -- это когда есть баг, который не могут найти месяц, а потом кому-то среди ночи решение во сне приходит. Во это озарение.
Приведу пример из школы: есть класс, есть урок родного языка. Есть задача написать за 45 минут сочинение на некую тему. По итогу напишут его все равно все, хуже или лучше, но все. Однако, кто-то начнет писать сразу и у него повествование начнет выходить "прямо из под пера", а кто-то задумается минут на 10 и, возможно, несколько строчек накарябает в черновике, зато потом начнет строчить не останавливаясь, потому что у него уже все продумано.
Оценивать же нужно результирующее сочинение, а не процесс его написания.
Тогда как вы, говоря про "Лично мне всегда интересно как человек будет подходить к решению" ставите во главу угла именно процесс, а не результат.
Hlad
04.10.2023 03:48+1Я конечно не вот чтобы программист, но откуда в задаче 0xA вообще берётся переменная i, если аргумент функции - l (строчная L)?
Но в целом да, задачки забавные. Реально решил только первые четыре...
vic-35
04.10.2023 03:48+2Хорошие у вас задания.
Про 0х3... уже писали , предложу уточнение по 0х5...
Правильное решение на мой взгляд у вас страдает избыточным выделением памяти. Что бы нам мешало написать
display_string(str_func().c_str())
и временное значение сохраняется до окончания выражения по стандарту.
sshmakov
04.10.2023 03:48+10x1
Первый элемент массива пропущен
Потому что вторая sayHello написана с использование юникода, который к сожалению не всегда палится.
Занятно, что подсветка синтаксиса на Хабре попалила второй sayHello.
dalerank Автор
04.10.2023 03:48первым элементов была сама сказка :)
dalerank Автор
04.10.2023 03:48подсветку отключил, давайте уровняем возможности редакторов хабра и vs. В 90% говорили код не соберется и всего несколько человек попросили ноут
eao197
04.10.2023 03:48+1Я правильно понимаю, что вы из игростроя?
dalerank Автор
04.10.2023 03:48-1Много откуда, пока что кот столуется у игроделов (https://habr.com/ru/articles/751706/)
eao197
04.10.2023 03:48+2Игрострой накладывает какой-то собственный отпечаток на то, какие вопросы задают на собеседовании.
SeregaSA73
04.10.2023 03:48+2Проблема всех Российских компаний в том что они хотят что бы ты был и шнец и жнец и трубе игрец, узкий специалист сделает работу быстрее и качественнее.
mikronavt
04.10.2023 03:48+2С узкими специалистами куча проблем. Со стороны руководства: 1)комплектовать команду узких специалистов сильно сложнее 2)делать равномерную загрузку узких специалистов значительно сложнее 3)при нехватке/перегрузке узких специалистов по какой-то одной вещи может встать много задач, которые в нее упираются 4)задачи, требующие знаний в разных областях, некому решать. Со стороны спеца: 1)сложнее искать работу, потому что в нужный момент спрос на рынке может быть слишком мало подходящих вакансий 2)знания могут устареть, и тогда углубление в технологию будет напрасным 3)может быть банально скучно копать что-то одно все время.
Sazonov
04.10.2023 03:48+11Вы знаете, я плюсанул обе статьи, спасибо. Но когда мне на одном собеседовании начали задавать такие задачки, мол что делать с таким кодом, как его исправить, я в какой-то момент не выдержал и сказал: бить линейкой по рукам того кто так пишет (уточнив что это шутка, но тем не менее). И добавил: если у вас такой код в продакшене, то вы не умеете C++ и нам с вами не по пути. Предложил на этом закончить собес и поблагодарил за вкусный кофе.
Допускаю что можно спросить что-то одно, больше ради шутки, но в целом такие вопросы вызывают раздражение.
Был недавно на нескольких собесах. На одном сначала спросили про паттерн стратегия с минимальным примером. А потом начали спрашивать, знаю ли я зачем нужно слово virtual. Мы с компанией друг другу не подошли, тем более что они предложили сильно меньше моих ожиданий для той позиции. На другом собесе (на senior) позицию самым сложным оказалось написать тривиальную функцию уровня самой простой задачи с leetcode. Но на своей практике я знаю что намного важнее уметь проектировать и писать удобный для переиспользования и читаемый код, чем реализовывать определенные алгоритмы (с этим и сильный джун справится).
dalerank Автор
04.10.2023 03:48-1Определенно, поэтому сказочные собесы я надеюсь отмирающий вид. А вопросы как-то сами накопились из разных источников.
SpiderEkb
04.10.2023 03:48Но на своей практике я знаю что намного важнее уметь проектировать и писать удобный для переиспользования и читаемый код, чем реализовывать определенные алгоритмы (с этим и сильный джун справится).
Вот именно. А еще важно выбрать правильный подход к решению задачи. Возможно, он будет не самым простым в реализации, но даст наилучший результат. Это уже задача разработчика а не кодировщика со знанием алгоритмов.
Sazonov
04.10.2023 03:48Главный вопрос - как это проверить на собеседовании. Далеко не у всех разработчиков есть пет проекты, которые можно поковырять прямо во время собеседования.
dalerank Автор
04.10.2023 03:48Поэтому на первый план выходят адекватность и "отделопригодность"( называйте как хотите - умение работать в команде, личная приязнь, неконфликтность и тд), если рокстар будет писать код/принимать решения/ставить задачи, от которого страдают все, то будет плохо в итоге всем. И рокстару тоже.
event1
04.10.2023 03:48+1я в какой-то момент не выдержал и сказал: бить линейкой по рукам того кто так пишет
Мне захотелось дать лопатой по голове уже просто после прочтения первых трёх заданий. Боюсь представить, что будет если такое предложат на интервью
dalerank Автор
04.10.2023 03:48-1чем третье не угодило? видел такое несколько раз в пром коде. Было интересно когда же в проде наткнутся
event1
04.10.2023 03:48+2Использование операций пред- и пост-инкремента в программах не рассчитанных на исполнение на компьютерах серии DEC PDP (либо других системах с автоинкрементом) является одним из смертельных грехов программирования. Наряду с закомментированным кодом, использованием оператора goto и т.п. Ещё как-то можно оправдать код, где эта операция составляет целое выражение. Но, то что предложил автор — это конечно ни в какие ворота. Он сам и указал причину: никто не может заранее предсказать, какой будет результат выполнения этого кода.
iig
04.10.2023 03:48видел такое несколько раз в пром коде
Интересно зачем так делать. Экономия строчек в исходнике?
KanuTaH
04.10.2023 03:48+3Однако, поскольку поскольку указатель в файле остался на прежнем месте в конце, то попытка прочитать данные из конца файла приведет к UB.
К какому же UB это приведёт, если поведение в данном случае вполне определено?
astrowander
04.10.2023 03:48+70х2...
if (my_legs < 0) { return -my_legs; }
Что вернёт функция, если my_legs == - 2^31?
HolyBlackCat
04.10.2023 03:48+2В 0x4 начиная с C++20 не нужен
std::cin.width()
, все работает само: https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt2В 0x8, мне кажется, любой нормальный человек использовал бы
wait_for
, принимающий лямбду с условием, хотя это и не "одно слово".В 0xC
__end
- зарезервированное имя, http://eel.is/c++draft/lex#name-3
piton_nsk
04.10.2023 03:48+1Я не плюсовик, но вообще -my_legs для INT_MIN должен выкинуть исключение. И как оно вообще компилируется, если там не для всех веток return есть? Или в плюсах это норма?
lgorSL
04.10.2023 03:48К сожалению - компилируется. Обратная сторона медали: в языках где
return
требуется во всех ветках, может не компилироваться валидный код типа `f() { while (true) {return 1;} }`. В таких местах приходится дописывать избыточный return.
Но лично мне более удачным кажется выбор языков, где return требуется везде - больно уж легко его случайно потерять.
owlpower
04.10.2023 03:48Про INT_MIN: Если бы так было, то на каждый вызов наворачивались бы лишние скрытые проверки. В случае если заранее известно что входной my_legs из определённого валидного диапазона это просто резало бы перф... A так да - это UB.
code_panik
04.10.2023 03:48+14"Расскажите, как вы бы пояснили своей бабушке, что такое мьютекс?"
Маленькое окошко, в которое помещается только одна голова в регистратуре.
blind_oracle
04.10.2023 03:48+2Какое же минное поле эти Плюсы, не устаю поражаться... UB там, UB тут, о, и здесь тоже он.
И каждые три года вот вам новый стардарт со свежими UB.
Зато обратная совместимость с Си.
dalerank Автор
04.10.2023 03:48-1А вы думаете соседнее поле с бемолями менее минное?
blind_oracle
04.10.2023 03:48Ну, я последние лет 10 писал на Go/Rust и там с этим вроде как всё сильно получше, если не лезть во всякие
unsafe
qw1
04.10.2023 03:48+1Конечно.
Там запрещено забывать return в каких-то ветках функции.
Там невозможно обратиться к неинициализированной переменной.
Разыменовывание null даёт вполне определённый вид исключения, а не UB.
и т.д...
knstqq
04.10.2023 03:48+2это что, в новом C++23 добавили специальную функцию которая делает UB по требованию разработчика!
std::unexpected
на случай если разработчик не может придумать как сделать UB :)https://en.cppreference.com/w/cpp/utility/expected/unexpected
dalerank Автор
04.10.2023 03:48все для людей, Комитет не дремлет. @Antoshkaно зачем?
knstqq
04.10.2023 03:48там на cppreference написано же. Чтобы позволить компилятору писать более оптимальный короткий ассемблерный код
например,
int abs_legs(int my_legs) {
if (my_legs < 0) {
return -my_legs;
}
std::unexpected();
}
это подсказка компилятору что abs_legs() никогда не будет вызвана с положительным аргументом; с другой стороны читающий код тоже видит что в std::unexpected() никогда нить выполнения не попадёт (по крайней мере не должна) в std::unexpected(). Но при этом в случае my_legs - отрицательный (как ожидается), то код будет абсолютно валидным
blind_oracle
04.10.2023 03:48Напоминает макросы
likely
/unlikely
в ядре Linux. Да и в крестах вроде что-то такое было вроде, аннотации типа[[likely]]
Хотя нет, они чисто для branch prediction, а тут вообще "второй бранч не будет вызван никогда". Но, по идее, надо просто такой код не писать...
knstqq
04.10.2023 03:48Да, похоже, но есть отличие. Likely используется в случае если нужно указать компилятору более вероятную ветвь для более эффективного когда.
В случае "std::unreachable" вообще никакого кода сгенерировано не будет на этот случай (или будет), на то это UB.
Andrey2008
04.10.2023 03:48Это было интересно. Обычно, я что-то такое пишу. А тут довелось самому поиграть в угадайку. Спасибо.
Кстати, пример с
int a = index + pockets[++index];
напомнил мне про наш один вопрос - Глубина кроличьей норы или собеседование по C++ в компании PVS-Studio :)P.S. А кому интересно узнать про глубину глубин с Unicode, приглашаю сюда - Атака Trojan Source для внедрения в код изменений, незаметных для разработчика.
dalerank Автор
04.10.2023 03:48+3про багу с юникодом я узнал, когда два рокстара в компании мерялись
размерами компиляторовсилушкой богатырской, попутно был сломан ревью тул и лег репо сервер, оказалось что гит не может прочитать такое в боди и сигфолит на этом комите(было это в 2018 году)
knstqq
04.10.2023 03:48тут в 0x2 две проблемы на самом деле
int abs_legs(int my_legs) {
if (my_legs < 0) {
return -my_legs;
}
}
кроме `return my_legs` в случае если часло положительное нужно ещё расмотреть случай когда my_legs == std::numeric_limits<int>::min()
не отрицательные числа могут быть представлены в виде положительных, а именно -2^31 при отрицании будет ОПАНА ПЕРЕПОЛНЕНИЕ. А переполнение не просто переполнение, а именно Undefined Behaviour и это больно
knstqq
04.10.2023 03:48тут в 0xC тоже две проблемы:
for (auto format = begin(formats), __end = end(formats); format != __end; ++format) {
переменные начинающиеся с двух знаков подчёркивания зерезервированы для стандартной библиотеки и внутренного использования. Поэтому __end ПЛОХО!
manfredima
04.10.2023 03:48+2Собес - Социальное обеспечение (абр. собес) — организованная государством форма помощи для утверждённого круга лиц, оказываемая при наступлении определённых юридических фактов, в установленных законом ситуациях, с целью выравнивания социального положения граждан.
wataru
04.10.2023 03:48+1Однако, поскольку поскольку указатель в файле остался на прежнем месте в конце, то попытка прочитать данные из конца файла приведет к UB
Errors are signaled by modifying the internal state flags, except for (3), that never sets any flags (but the particular manipulator applied may):
eofbit: The input sequence has no more characters available (end-of-file reached).Его можно настроить бросать исключения, но это никогда не будет UB.
alex103
0x3
исправленный вариант, по моему, не делает то же самое, что оригинал
наверное должно быть что то типа:
int a = index;
++index;
a = a + pockets[index];
SpiderEkb
Вот тоже так показалось.
Как вараинт
поскольку index передается по значению и что с ним делается внутри функции наверх не пробрасывается (т.е. от нас не ждут что index после вызова функции увеличится).
Хотя именно такие вещи несколько бесят - не должна оптимизация ломать логику. Поскольку явного приоритета операций тут нет, выполняться они должны слева направо. Т.е.
Присваиваем a значение index
Увеличиваем index на 1
Прибавляем к a значение pockets[index]
Любое другое поведение есть нарушение порядка операций.
haqreu
Без понятия sequence point трудно определить правильный порядок операций. Так что не нарушаем его, и не будет никакого UB.
SpiderEkb
Где-то сказано что получение значения элемента массива имеет приоритет над сложением? Нет. Значит эти операции равноприоритетны. И выполняться должны именно в том порядке, в каком указаны. И
и
должны гарантированно давать разный результат. Вне зависимости от настроек оптимизации.
Иначе начинается угадайка "обмани компилятор".
qw1
ага, щас https://godbolt.org/z/eccT9hfP8
Эта строка — UB, и компилятор может вычислять её как угодно
https://en.cppreference.com/w/cpp/language/eval_order
vyatkh1
del