Как стать автором
Обновить

Комментарии 108

Через некоторое время при проведении собеседования вспомнилась эта шутка.

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

Да не сказал бы. Посмотреть тот же hh и сравнить вакансии c++ и какой-нибудь node.js

НЛО прилетело и опубликовало эту надпись здесь

Ну, я думаю что в случае ключевых слов JS и работа — node.js первое, что приходит в голову. Все эти angular/reactive/matreshka, которые через здоровенный автоматизированный пайплайн выдают пользователю кнопочку "сделать хорошо" на одностраничнике. Мало кого сейчас интересуют рисователи кнопочек на jQuery, если вообще интересуют.

НЛО прилетело и опубликовало эту надпись здесь

Аналогично PHP работает на серверной стороне, да и также как и PHP по большей части нацелена на веб. Можно конечно проворачивать такое и с c++, но едва ли такая вакансия будет настолько же оплачиваема и востребована как знаток ноды или того же пхп.
От минусанувшего хотелось бы узнать что не так с предыдущим комментарием.

Перепроверил — признаю, был неправ.
Сама по себе задача простая без вопросов, вполне можно развлечь себя ей. Однако — смотрите, как бы вы прокомментировали собеседование, на котором кандидату ( видимо на должность C программиста ) задавали бы вопросы про оператор «запятая», сложение числа со строкой и приоритет операций? Готов человек блестяще решивший эту задачку писать операционные системы, базы данных или какие-нибудь cache oblivious алгоритмы? Ведь сейчас перед программистами С стоят уже в основном действительно сложные и нетривиальные задачи ( все простое пишем на питоне).

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

А можете пояснить, что вы подразумеваете под понятием "сегментам" на ARM,
и ARM с MMU или с MPU имеется ввиду?

НЛО прилетело и опубликовало эту надпись здесь

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


Меня интересовало как раз что за сегменты (на arm скажем никаких сегментов в терминах i386 нет) и что за дырки между ними, но судя по все это было просто недопонимание.

НЛО прилетело и опубликовало эту надпись здесь
-mstructure-size-boundary для gcc не спасает?
НЛО прилетело и опубликовало эту надпись здесь
У меня софт крутится на x86, x86_64, MIPS, ARM. Структурки выравниваются по байту.
На одном компиляторе используется этот ключ, тк прагма не работает. И ничего не крашится.
НЛО прилетело и опубликовало эту надпись здесь
На то он и тест на уровень знания. Он не дает ответа «ДА» или «НЕТ». Тут много исходов. И обычно тесты не является основанием приема на работу или отказа, а просто тема для разговора.
на котором кандидату ( видимо на должность C программиста ) задавали бы вопросы про оператор «запятая», сложение числа со строкой и приоритет операций? Готов человек блестяще решивший эту задачку писать операционные системы, базы данных или какие-нибудь cache oblivious алгоритмы

Так это совершенно разные проверки. Если вам нужен человек знающий хорошо язык XYZ и при этом владеющий дополнительно навыками A, B, C, проверять вы будете и то, и другое, верно?
Тут три момента:
1). Категорически неочевидно как из блестящего владения оператором «запятая» следует хорошее знание языка C
2). Кроме того само по себе знание C не нужно никакому бизнесу, кроме разработчиков компиляторов. «Знаешь C? Ну, возьми с полки пирожок. Нам интересно как ты поможешь нам в разработке нашего вэб-сервера».
3). Собеседование — это формат переговоров, весьма сжатый по времени. В текущей реальности только гугл и сопоставимые компании могут мурыжить кандидатов 6-ю собеседованиями, для более приземленных компаний нужно за час-полтора принять решение о, том, будем ли мы сотрудничать с этим человеком. Тратить это время на разговоры об операторе запятая — это похоже на «Окей, леди и джентльмены, у нас час на то, чтобы заключить эту важную для всех нас сделку, так что давайте для начала станцуем танец маленьких утят»
1). Теперь и Вы знаете кун-фу оператор «запятая»
2). Нужно, если работаете программистом на С. Вы не поверите, но есть устройства, где язык С единственный язык программирования.
3). Согласен.
к п.2) да, еще про ассемблер забыл
2). Видимо не совсем четко сформулировал. Даже если программист пишет код на C на платформе, на которой кроме C ничего нет, платят ему не за то, что он печатает «int main()» и т.д., а за то, что он заставляет эти несчастные микроконтроллеры контролировать станки, собирать данные о температуре и так далее. Само по себе знание С ничего не стоит, оно должно быть частью гораздо более широкого набора умений. А вот это уже оплачивается
Угу! Полностью поддерживаю. За знание языка не платя, если не даешь результат. Язык это средство в достижении конкретной задачи. А эта задача — средство достижения задач других уровней.
Фундаментальные мировоззренческие вещи затронули.
Мне нравится сравнение программирования с написанием стихов на японском. Японский знать, конечно, надо, но это не главный навык.
1). Категорически неочевидно как из блестящего владения оператором «запятая» следует хорошее знание языка C

Никак. Необходимое, но не достаточное условие.

2). Кроме того само по себе знание C не нужно никакому бизнесу, кроме разработчиков компиляторов. «Знаешь C? Ну, возьми с полки пирожок. Нам интересно как ты поможешь нам в разработке нашего вэб-сервера».

Ну, наверно, если гражданин нанимает работников, проверяя знание языка Си, то именно им он как раз и нужен, не? Знание этого языка является необходимым условием для приема на работу именно в эту фирму. Необходимым, т.е. проверка на знание A, B и C из моего предыдущего комментария последует отдельно.
Собеседование — это формат переговоров, весьма сжатый по времени. В текущей реальности только гугл и сопоставимые компании могут мурыжить кандидатов 6-ю собеседованиями, для более приземленных компаний нужно за час-полтора принять решение о, том, будем ли мы сотрудничать с этим человеком.

Даже в маленьких конторах проводят по 2-3 собеседования, это нормально.
Никак.

Итак, мы проверяем знание оператора запятая, хотя из этого мы не можем извлечь никакой информации о знании языка C. Вроде бы как напрашивается вывод…

Ну, наверно, если гражданин нанимает работников, проверяя знание языка Си, то именно им он как раз и нужен, не?

Не. Это как: «Я взял на улицу зонт, следовательно идет дождь» — не следует.

проводят по 2-3 собеседования, это нормально.

У меня другое мнение и оно подтверждается ( моей ) практикой, но это уже совсем оффтопик. В любом случае, даже если у нас 3 собеседования — тратить время просто так не надо.
Итак, мы проверяем знание оператора запятая, хотя из этого мы не можем извлечь никакой информации о знании языка C. Вроде бы как напрашивается вывод…

Вы непоследовательны и нелогичны.
Я сказал, что это необходимое, но не достаточное условие. Вы понимаете, что это значит? Это значит, что мы проверяем, не отсутствует ли данное знание у человека, и выбраковываем по факту отсутствия в качестве одного из этапов.

Не. Это как: «Я взял на улицу зонт, следовательно идет дождь» — не следует.

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

На C нужно драйвера писать. И да, кто-то переписывал серверную обработку "космических боев" с PHP на C++.

Как человек, который по работе больше код читает, чем пишет, могу сказать, что ныряние в Си вызывает наибольшее отвращение. Даже не по причине низкоуровневости, а по причине того, что вместо понятных концепций (что делает инструкция), каждый вздох на си — это поток сайд-эффектов (хороших и плохих), которым позавидует даже брейнфак.
Новый код в этом плане уже получше. Вообще, на мой взгляд, почти в любом проекте главное, что требуется от разработчика — читаемость кода. А с таким подходом можно и на C нормально написать.
C понятен и прямолинеен. Смотришь на код и понимаешь, чего ждать в отладчике.
Вы нутро Asterisk смотрели?
Ну, из кода Asterisk можно узнать по крайней мере одну важную вещь
Скрытый текст
/* Mark: If there's one thing you learn from this code, it is this...
Never, ever fly Air France. Their customer service is absolutely
the worst. I've never heard the words "That's not my problem" as
many times as I have from their staff -- It should, without doubt
be their corporate motto if it isn't already. Don't bother giving
them business because you're just a pain in their side and they
will be sure to let you know the first time you speak to them.
If you ever want to make me happy just tell me that you, too, will
never fly Air France again either (in spite of their excellent
cuisine).
Update by oej: The merger with KLM has transferred this
behaviour to KLM as well.
Don't bother giving them business either...
Only if you want to travel randomly without luggage, you
might pick either of them.
*/

здесь
Много чего смотрел, написанного на С. Это очень маленький и простой язык, в нём легко разобраться. Если вы видите оператор + или ->, можете быть уверены на 100% в том, что будет сделано.
Значит вы не писали модули для Asterisk.
#define + -
#define -> .

?

Первая строка: на работе никогда такого не видел, это из разряда вредительства.
Вторая строка: после первой же сборки вражеская закладка будет обнаружена.
после первой же сборки вражеская закладка

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


разобраться в С просто. а вот в коде на С — посложнее будет

Согласен, это кажется бредом, но загляните в сишный код какой-либо старой библиотеки. Вы измените свою точку зрения. Среди железячников такого кода масса. Руки бы им оторвать за такой код.
А это не проблема Си. Железячники зачастую пишут отвратительный код на любом языке, просто в силу другой специфики.
Как начинавший с машинного кода и ассемблера DEC-овских машин, в языке С вижу только положительные моменты. Всё просто и понятно. И почему С такой каким его задумали.
Вот за эти заковырки многие и любят/ненавидят (кому что больше нравится) С/С++
Странно. Я пишу на более-менее современном С++, и считаю, что язык С знаю не очень хорошо (как и некоторые заморочки С++). Тем не менее, ровно за 3 минуты написал обвязку, с которой приведенный фрагмент компилируется и даже не падает в рантайме. Это я не для похвастаться, это я к тому, что уж слишком просто :)

Но есть нюанс: я считерил. Вообще не пришло в голову, что Р можно сделать функцией. Почему-то сразу подумал про макрос, и написал
#define P(x) 0
(хотя макросы не люблю и без крайней необходимости обычно не использую).
Вариант
Из той же оперы в Python:

[r'% r',][~0] % {(): '''^''' uR'|' in 2**2*__name__[::-8//(lambda _:~_)(3 or 2)]*2}<2>3j,(`3`) is ([])

Кто-нибудь скажет, не запуская, чему это будет равно? (отсюда)
Вообще-то, если попытаться запустить, то падает с ошибкой
False?
2 минуты http://cpp.sh/3pmon

Эк вас занесло. 3 секунды: http://cpp.sh/6wx7

вы читаете мои мысли

Подумал то же самое. Но вы и их прочитали :D

Оптимизация хвостовой рекурсии с аккумулятором? Круто, давно пора, я наверное еще 2 года назад удивлялся, что ее нет. Самое сложное — представить как можно больше паттернов рекурсий таким образом. Возможно, даже все представимы


Только что-то из патча не совсем понятно, как это сделано. Просто присвоили одному хитрому макросу результат другого хитрого макроса ???

А, так это первоапрельская шутка… Жаль, а ведь можно было реально сделать подобную оптимизацию (с рекурсией, а не отправкой тел в /dev/null)

Кстати, весьма полезная «оптимизация»: иногда возникает задача создать библиотеку-заглушку для линковки
#define for(...)
#define P(...)
int main() {
    for(;P("\n"),R--;P("|"))for(e=C;e--;P("_"+(*u++/8)%2))P("| "+(*u/4)%2);
}

Интересно, что понимается под словом "работает".

Решение отличное! Дурацкие задачи можно и нужно решать дурацкими способами!
int main() {/*
    for(;P("\n"),R--;P("|"))for(e=C;e--;P("_"+(*u++/8)%2))P("| "+(*u/4)%2);
*/}
еще return 0; добавить можно
Вот ещё! И так скомпилится.

Вообще, это — правильный подход на собеседовании: такие куски надо выпиливать без жалости. Повезёт — по тестам сразу восстановить функционал можно. Не повезёт — лучше раньше начать разгребать, чем в ночь перед релизом дебажить такое.
Блин, я специально не раскрывал подсказки. Думал, тут есть содержательный смысл и все эти вызовы
void P(const char *s){
    printf("%s", s);
}
в итоге напечатают красивую картинку в консоли. Понятно, что если сделать
char *u,

то можно разными строками получать разные картинки. Я пытался сделать
int k = 8; /* 0, 1, 2 .. 16 */
int *u = &k;

Ничего красивого не вышло. Потом прочитал спойлеры и разочаровался. И неинтересно, и не смешно. Чувствую себя обманутым! После такого собеседования я бы и сам к вам на работу не пошел!
Аналогично — думал, какие надо угадать значения R, C, и куда направить u, чтобы получилась вразумительная картинка. Остальное все было как-то почти очевидно (опыт кодгольфа дает о себе знать).

Вопрос к автору — я вместо функции P сделал так:
#define P(x) printf(x)
Это как-то карается? А еще у меня ни ретурнов, ни инклюдов — ворнинги сыплются, но все работает корректно (опять же, опыт кодгольфа).
НЛО прилетело и опубликовало эту надпись здесь
В данном конкретном примере все вызовы P принимают строковые литералы со смещением 0 или 1, так что все вполне управляемо.

Да, это main, поэтому я опустил return.

Функции из libc не имеют отношения к инклюду — инклюд влияет только на подтягивание объявлений функций. Фокус в том, что printf (и поразительно большое число других функций) имеют прототип, эквивалентный прототипу по-умолчанию — возвращают int, принимают то, что в них передали.
Спасибо!
Я знаю только одного человека, который сделал это задание сразу. (И это был не я)

И этот человек догадался, что этот код «рисует лабиринт в stdout»? Лично у меня, когда я увидел эту строчку, возникло желание не понять, что оно делает, а тупо нейтрализовать и просто удовлетворить формальным требованиям (это несложно сделать даже без макросов):


int main() {
    int R = 0;
    int e = 0;
    int C = 0;
    char *u = 0;
    int (*P)();

    for( ; P("\n"), R-- ; P("|"))
        for(e=C; e--; P("_" + ( *u++/8)%2))
            P("| " + (*u/4)%2);

    return 0;
}

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

Из этого кода, если хоть чуть-чуть вчитаться, очевидно, что он что-то рисует в stdout.

Да, просто «тупо нейтрализовать». Лабиринт не нужен. Немного скучноватый вариант вышел (тело циклов не выполняется), но
int (*P)();
хороший показатель (одного этого достаточно).
Здесь нужен собеседник, который подскажет. Да, это не шутка. u — не одно число, а массив (u++ — гуляет по нему).
Вовсе не обязательно.
int k = 0;
int *u = &k;
void P(const char *s){
	printf("%s", s);

	if (*s == 0 || *s == '_'){
		*u--;
		(*u)++;
	}
}

Практически на рефлексе: слова, написанные целиком капсом — макросы. Сразу после этого заставить пример компилироваться и ничего не делать (или делать всё, что нам заблагорассудится) — тривиально (аргументы макросов можно не использовать вообще — пример резко упростится).


Но разбор прочёл не без интереса.

// Example program
#include <stdio.h>

extern "C"
{

int R = 5, *u, e, C, x = 5;

void P( const char * ptr )
{
    C = R;
    printf( "R:%d, u:%p, e:%d, C:%d, x:%d ptr:%p '%x,%x,%x''%c%c%c'\n"
           , R, u, e, C, x, ptr
           , *(ptr+0), *(ptr+1), *(ptr+2)
           , *(ptr+0), *(ptr+1), *(ptr+2) );
    return;
}

int main()
{
 
 u=&x;
 
 for ( ; P("\n"), R--; P("|"))
    for (e = C; e--; P("_" + (*u++ / 8) % 2))
        P("| " + (*u / 4) % 2);
}

}
Да, я так первый раз и написал, но u — не одно число, а массив. (u++ — гуляет по нему)
Лучше С задавать не в функции P.
Это понятно. Мне просто больше нравятся выражения "|/-\"[ i++ % 4 ] для таких извращений.
Думаю, если на собеседовании попадётся такое задание, то правильным ответом будет «Для начала, узнать кто это написал, и если этот человек ещё работает в компании, найти его и долго бить томиком книги за авторством Кернигана-Ритчи».
Найти автора я не смог, но похоже это реальный код. Что-то в виде таблицы рисовалось.
Возможно, его нашли раньше вас ).
Нет, действительно, писать всё в одну строчку, плюс все эти *p++, это чистой воды издевательство на тем, кто этот код будет разбирать.
Ну и в припадке ностальгии, вспомнил знатный холивар с коллегой, который был за if( some ) вместо if( some != 0 ). Хотя, это уже не так страшно и воспринимается более менее нормально.
Раньше компиляторы были не такими умными, и в код вставлялась константа.
Если написать без "!= 0" то машинный код становился короче. Отсюда такой подход.
возможно, но скорее Print

Вы потеряли пробел в строке "_ ", что не лучшим образом сказывается на понимании, что вообще происходит.

Нет, не потерял. Указатель может указывать на конец строки. Тогда будет пустая строка.

Меня смутило то, что во внутреннем цикле вызывается


P("| " + BIT_SET(*u, 2));
P("_" + BIT_SET(*u, 3));
u++;

Вроде бы конструкции однотипные, но без пробела логика получается подозрительно разная.


Так вот, смутило это меня в достаточной степени, чтобы найти


оригинальный код

Это один из победителей IOCCC 1985, shapiro.c by Carl Shapiro, код рисует лабиринт в stdout. И да, там есть этот самый пробел. =)


В процессе поиска нашелся еще и занятный пост про обфускацию C и лабиринты.

Мощь и сила! Спасибо! Я теперь знаю автора
По-моему этот же исходник был подписью автора в FIDO

только почему-то мой склероз в свое время мне подсказывал maze.c а надо было shapiro.c искать

Прочел код, не понял, где там шутка. Вполне все тривиально.
Если считать, что это задача на C++, то можно просто перегрузить все операторы. Тогда уж можно что хочешь складывать и инкрименировать.
И в Си можно складывать и инкриментировать.
#include <iostream>
using namespace std;

void P(const char *str) {
     cout << str << endl;
}

int main() {
    int e; 
    char *u = "11111111111111";
    int R = 3; 
    int C = 5;
    for(; P((char *)"\n"),R--; P((char *)"|")) {
    for(e=C; e--; P("_"+ (*u++/8)%2)) {
        P(" | "+ (*u/4)%2 );
    }
    }
   return 0;
}

Выводит:


| _ | _ | _ | _ | _|
| _ | _ | _ | _ | _|
| _ | _ | _ | _ | _|
Да, только:
1. что-то с отступами не задалось
2. это С, а не С++ (см. функцию Р)
3. код править по заданию нельзя. (char *) — зачем?
Что-то не верю я в то, что она такое выводит.
Слегка подправил код товарища:
#include <stdio.h>

void P(const char *str) {
    printf("%c", *str);
}

int main() {
    int e;
    char *u = "11111111111111";
    int R = 3;
    int C = 5;
    for (; P("\n"), R--; P("|"))
        for (e = C; e--; P("_" + (*u++ / 8) % 2))
            P("| " + (*u / 4) % 2);
    return 0;
}
Таки выводит, правда, без лишних пробелов (см изменения в P(char*)).
(см изменения в P(char*)).
Спасибо, да, я забыла << endl убрать.

3. код править по заданию нельзя. (char *) — зачем?
А, его тоже забыла убрать до этого аргумент в функции был не const, поэтому была ошибка
invalid conversion from 'const char*' to 'char*'

Теперь берёте на работу?)


image

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

НЛО прилетело и опубликовало эту надпись здесь
1. Спасибо, да ошибся. Поправил.
2. Что-то не понял, где «C» декрементируем?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации