Здравствуйте, amberovsky, Вы писали:
A>Здравствуйте. A>При попытке устроится в одну фирму прислали "удалённое" тестовое задание.
A>
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>
void g( const char *, const char *, size_t );
void f( size_t n )
{
сhar *a = NULL;
char *b = NULL;
if (n <= 0)
return;
try
{
a = new char[n];
b = new char[n];
g( a, b, n );
}
catch (...)
{
if (a) delete [] a;
if (b) delete [] b;
}
if (a) delete [] a;
if (b) delete [] b;
}
какие авто_птр?? какой буст??? Зачем усложнять жизнь в тривиальном куске кода? KISS!
A>
A>2.
A>int main(int argc, char* argv[])
A>{
A> if( argc > 1 )
A> printf( argv[1] );
A> return 0;
A>}
A>
Не смущает, что нумерация массива начинается с 0, и в argc[0] == 'имя исполняемого файла'?
Т.е. argc всегда > 0
Здравствуйте, Kernan, Вы писали:
К>>auto_ptr и boost — это как раз и есть воплощение KISS, в отличие от мосвелосипедстроймонтажа. K>Каки бусты/stl в 3-х строчках кода, а? Надеюсь, ты не из тех кто начинает использовать стратегии, где достаточно православного switch?
Привет участникам синтаксического оверхеда(tm)!
#include <vector>
void f( size_t n )
{
сhar *a = NULL; std::vector<char> a(max(n,1));
char *b = NULL; std::vector<char> b(max(n,1));
if (n <= 0) //return; //try//
{ //
a = new char[n]; //
b = new char[n]; //
g( a, b, n ); g(&a[0], &b[0], n);
} //catch (...) //
{ //if (a) delete [] a; //if (b) delete [] b; //
} //if (a) delete [] a; //if (b) delete [] b; //
}
R>Если вылетит исключение при выделении памяти под b, то память для a не будет освобождена -> утечка память.
Ну какая утечка? Если вылетит исключение в приведенном фрагменте, программа просто завершится и всю память почистит ОС.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, amberovsky, Вы писали:
A>>
A>>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>>1.
A>>void g( char *, char *, size_t );
A>>void f( size_t n )
A>>{
A>> char *a = new char[n];
A>> char *b = new char[n];
A>> g( a, b, n );
A>> delete [] b;
A>> delete [] a;
A>>}
A>>
A>>Я ответил так: A>>1. new может не сработать, нужно добавить, например, try/catch. Кроме того, g может "испортить" указатели (как тут просто решить я не знаю)
R>Если вылетит исключение при выделении памяти под b, то память для a не будет освобождена -> утечка память. R>Ну и плюс в целом ручное управление памятью чревато проблемами при поддержке, поэтому кошерно будет:
R>
R>void f( size_t n )
R>{
R> std::auto_ptr<char> a (new char[n]);
R> std::auto_ptr<char> b (new char[n]);
R> g( a.get(), b.get(), n );
R>}
R>
Тут UB.
R>Если доступен scoped_ptr<>, то лучше использовать его.
Ну, тогда уж
void f( size_t n )
{
std::vector<char> a (n);
std::vector<char> b (n);
g( &a[0], &b[0], n );
}
или
void f( size_t n )
{
boost::scoped_array<char> a (new char[n]);
boost::scoped_array<char> b (new char[n]);
g( a.get(), b.get(), n );
}
Здравствуйте, amberovsky, Вы писали:
A>[ccode] A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать? A>1. A>void g( char *, char *, size_t ); A>void f( size_t n ) A>{ A> char *a = new char[n]; A> char *b = new char[n]; A> g( a, b, n ); A> delete [] b; A> delete [] a; A>}
Тут по существу уже сказали. А меня в таком коде всегда прикалывает, почему два блока нельзя за раз выделить:
char *a = new char[n*2];
char *b = a + n;
g( a, b, n );
delete [] a;
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Kernan, Вы писали:
К>>>auto_ptr и boost — это как раз и есть воплощение KISS, в отличие от мосвелосипедстроймонтажа. K>>Каки бусты/stl в 3-х строчках кода, а? Надеюсь, ты не из тех кто начинает использовать стратегии, где достаточно православного switch?
К>Привет участникам синтаксического оверхеда(tm)! К>
К> #include <vector>
К>void f( size_t n )
К>{
К> сhar *a = NULL; std::vector<char> a(max(n,1));
К> char *b = NULL; std::vector<char> b(max(n,1));
К> if (n <= 0) //
К> return; //
К> try//
К> { //
К> a = new char[n]; //
К> b = new char[n]; //
К> g( a, b, n ); g(&a[0], &b[0], n);
К> } //
К> catch (...) //
К> { //
К> if (a) delete [] a; //
К> if (b) delete [] b; //
К> } //
К> if (a) delete [] a; //
К> if (b) delete [] b; //
К>}
К>
К>Ну и у кого из нас три строчки?
вот только вопрос есть, тебе на вход приходит 0, а на вход функции g ты передёшь 1. Подмена понятий, однако. Хотя код неплох, есть чему поучится
Здравствуйте, amberovsky, Вы писали:
A>1. new может не сработать, нужно добавить, например, try/catch.
Лучше не try-catch (это слишком громоздко), а RAII — любой умный указатель или контейнер.
A> Кроме того, g может "испортить" указатели (как тут просто решить я не знаю)
Паранойя. Чтобы испортить указатели, функция должна расстрелять стек. А это никакими средствами не лечится.
А вот метнуть исключение функция g() может запросто.
Решение
void f(size_t n)
{
if(n!=0)
{
std::vector<char> a(n), b(n);
g(&a.front(), &b.front(), n);
}
else// если мы ничего не знаем про логику g(), попробуем максимально близко воспроизвести ситуацию
{
char a,b;
g(&a,&b,0); // передаём два уникальных ненулевых адреса! ибо new char[0] != NULL
// хотя, возможно, было бы достаточно
g(NULL,NULL,0);
}
// или, в обобщённом виде
std::vector<char> a(max(n,1)), b(max(n,1)); // гарантируем непустоту и уникальность
g(&a.front(), &b.front(), n);
}
A>2. В аргументе командной строки может встретиться символ % или \, что будет расценено printf как специальный символ. A>Если следующие символы совпадут с какой-либо спецификацией формата (для % или \), то в данном случае для % — UB, для \ — произойдёт подстановка.
Про бэкслеш — фантазия. Для принтфа спецсимволом является только %.
Здравствуйте, alexander_st, Вы писали:
_>чтобы испортить указатели ф-я не должна стрелять в стек, она должна всего лишь вызвать для них delete и лечится это передачей константных указателей
Не лечится Удалять указатели на константы можно.
Если уж позволено рефакторить программу, то нужно заменить сигнатуру g на что-нибудь такое
void g(std::vector<char>& a, std::vector<char>& b); // n не нужно, т.к. размеры переданы в составе объектов
// либоvoid g(boost::shared_array<char> a, boost::shared_array<char> b, size_t n)
void f(size_t n)
{
boost::shared_array<char> a(new char[n]), b(new char[n]);
g(a,b,n); // g может передать права собственности куда-то на сторону
// тогда по выходу из f() массивы останутся жить, как и задумано!
}
_>чем в данном контексте использование std::vector лучше, чем непосредственный указатели? К>>А вот метнуть исключение функция g() может запросто. _>но в вашем коде так же нет защиты от метания исключений
Вот как раз этим-то и лучше, что защита есть.
Вылет исключения — это частный случай выхода из блока. При этом разматывается стек, вызываются деструкторы.
А то, что я никак не перехватываю и не обрабатываю исключения (ни std::bad_alloc, ни, тем более, неизвестно что из недр g()) — ну так, от меня это и не требовалось... Тот, кто сможет, этажом выше, то и обработает. А я лишь обеспечу непротиворечивость своего участка системы.
Здравствуйте, Константин.
KC>В общем, побывал я в DrWeb на собеседовании и даю правильный с их точки зрения ответ: KC>По первому заданию:
Я рад, что обсуждение недостатков Вашего первоначального ответа пошло Вам на пользу. И всё же:
1. Хочу предостеречь от использования &a[0] без проверки на наличие в векторе хоть каких-то данных. Если мы не предупредили Вас об этой проблеме на собеседовании, это не значит, что её не существует.
2. Вся строчка "func f_drWeb said:" — это Ваши личные фантазии, к которым компания DrWeb не имеет ни малейшего отношения. Пусть они остаются на Вашей совести.
KC>По второму заданию:
Ваши упражнения с локалью пригодны только для одного случая: когда консольная программа вызывается с русскими аргументами в кодировке ANSI. Для случая "консольная программа вызывается из консоли" установка локали создаёт проблемы, а не решает их. На собеседовании Ваш ответ на этот вопрос вообще не обсуждался, и поэтому Ваши утверждения о причастности компании DrWeb к этому коду я решительно отвергаю.
KC>Кроме того, можете скачать http://piterludi.com/fordeveloponbox/stlfuns.zip KC>В этом проекте (VC++ 2008) я продемонстрировал также поведение виртуальных функций, вызываемых из конструкторов и деструкторов — вопрос, KC>который почему-то нередко возникает на собеседованиях. Также немного побаловался с промышленным дизайном для этой же задачи.
Вот этот фрагмент особенно порадовал:
void GFuncCaller_NOSTL::clearBuff()
{
if(!BufferA) delete[] BufferA;// Это не защита. Если BufferA==NULL, проблем с deleteA все равно нет
BufferA = NULL; // просто незачем вызывать delete
if(!BufferB) delete[] BufferB;
BufferB = NULL;
gBufferSize = 0;
};
У Вас действительно есть свой стиль написания кода. Вы только с названием стиля ошиблись: это не Industrial, а Trash.
KC>Хотя по ходу собеседования я и сделал вывод, что промышленный дизайн и общепринятые подходы они считают KC>ненужным усложнением и ретроградством. KC>Возможно, я ошибаюсь: все-таки собеседование проходило в стиле "опусти кандидата", KC>а при таком стиле язвительно критикуется любое высказывание.
У нас во время собеседования действительно возникли разногласия в том, какие подходы считать общепринятыми. Но не более того.
Что касается стиля "опусти кандидата" — мы честно высказывали своё мнение о Ваших высказываниях и честно давали Вам возможность отстоять свою позицию по тем вопросам, по которым мы были с Вами не согласны.
А вот, например, по второму вопросу из письменного задания мы Вас не "опускали", и вот Вы уже рассказываете, что это и есть правильный, по нашему мнению, вариант.
KC>Вероятно, предполагается, что осознавший свое ничтожество и полностью дезориентированный кандидат KC>преисполнится безумной радости, если ему все-таки сделают предложение поработать в такой новаторской фирме.
Пожалуйста, воздержитесь от обобщений. К нам и нормальные кандидаты приходят.
KC>Просто держите в голове, что "опустить" можно любого: конкретно моих "экзаменаторов" можно было бы легко "опустить" как минимум KC>по знаниям паттернов проектирования (особенно сoncurrency) и подходам к разработке сложных систем.
Вы говорите это после того, как сами на собеседовании продемонстрировали незнание http://en.wikipedia.org/wiki/Reactor_pattern? И Вам не стыдно?
KC>Никто не может знать всего.
Этого мы от кандидатов и не требуем.
KC>Если будете у них на собеседовании — в момент, когда от вас потребуют что-либо написать, постарайтесь не отвлекаться KC>на вопросы "экзаменаторов", "заторапливание" и попытки вывести вас из себя. Иначе наделаете таких ляпов, что самому потом будет стыдно.
...И постарайтесь не тратить по двадцать минут на вопросы типа "Эту задачу можно решить всего тремя строчками кода, а не тридцатью, как Вы предложили. Вы можете написать эти три строчки?".
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>
A>Я ответил так: A>1. new может не сработать, нужно добавить, например, try/catch. Кроме того, g может "испортить" указатели (как тут просто решить я не знаю)
Если вылетит исключение при выделении памяти под b, то память для a не будет освобождена -> утечка память.
Ну и плюс в целом ручное управление памятью чревато проблемами при поддержке, поэтому кошерно будет:
void f( size_t n )
{
std::auto_ptr<char> a (new char[n]);
std::auto_ptr<char> b (new char[n]);
g( a.get(), b.get(), n );
}
Если доступен scoped_ptr<>, то лучше использовать его.
Здравствуйте, SergeyT., Вы писали:
ST>Здравствуйте, amberovsky, Вы писали:
S>>>Ну, тогда уж S>>>
S>>>void f( size_t n )
S>>>{
S>>> std::vector<char> a (n);
S>>> std::vector<char> b (n);
S>>> g( &a[0], &b[0], n );
S>>>}
S>>>
ST>Это не очень красивая, но совершенно стандартная идиома. ST>Скотт Мейерс, Эффективное использование STL, Item 16
Ну нахрен такие идиомы. Добавишь ты в вектор новый элемент, сделает он реаллокацию внутреннего буфера, и указатель, который передавался в doSomething станет невалидным. А функция могла его где-нибудь сохранить. Конечно, как правило такой сценарий или не возникает или ничем не грозит, но один раз из ста, когда грабли срабатывают, бывает достаточно неприятным.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, amberovsky, Вы писали:
A>>1. new может не сработать, нужно добавить, например, try/catch. К>Лучше не try-catch (это слишком громоздко), а RAII — любой умный указатель или контейнер. К>Паранойя. Чтобы испортить указатели, функция должна расстрелять стек. А это никакими средствами не лечится.
чтобы испортить указатели ф-я не должна стрелять в стек, она должна всего лишь вызвать для них delete и лечится это передачей константных указателей
то биш
g(const char*, const char*, size_t);
только не надо предположений что можно сделать всякие const_cast`ы и спецом все на свете по удалять, вот это уже действительно паранойя
К>Решение К>
К>void f(size_t n)
К>{
К> if(n!=0)
К> {
К> std::vector<char> a(n), b(n);
К> g(&a.front(), &b.front(), n);
К> }
К> else// если мы ничего не знаем про логику g(), попробуем максимально близко воспроизвести ситуацию
К> {
К> char a,b;
К> g(&a,&b,0); // передаём два уникальных ненулевых адреса! ибо new char[0] != NULL
К> // хотя, возможно, было бы достаточно
К> g(NULL,NULL,0);
К> }
К> // или, в обобщённом виде
К> std::vector<char> a(max(n,1)), b(max(n,1)); // гарантируем непустоту и уникальность
К> g(&a.front(), &b.front(), n);
К>}
К>
чем в данном контексте использование std::vector лучше, чем непосредственный указатели? К>А вот метнуть исключение функция g() может запросто.
но в вашем коде так же нет защиты от метания исключений
Здравствуйте, KhConstantine, Вы писали:
>>> несколько десятков тысяч рублей чистого убытка KC>Понятен стандартный уровень ваших зарплат.
С точностью до десятичного порядка — да, понятен и отсюда (т.е. это не сотни тысяч рублей в месяц и не тысячи за всё время испытательного срока). Но заглянув в описание наших вакансий, Вы получили бы куда более точную оценку.
Вы считаете, что нам следует терять на своих ошибках больше?
>>> до того, как он сможет претендовать на работу у нас KC>Да, каждый сам устанавливает планку. И компании уровня "несколько десятков тысяч рублей"/брэнд из разряда "а разве они все еще существуют?" — тоже.
Не существует нас. И офис вам померещился, и собеседование. Сон это был. Переворачивайтесь на другой бок и спите дальше.
KC>В адаптированном виде описание применения транзакционного анализа нередко включается в ученики по управлению проектами, team building, etc KC>В соответствии с этой терминологией: Практически все транзакции идут у Вас с позиции "родитель критикующий". Позиция известна, как наиболее конфликтная и наименее продуктивная. Человек, общающийся с этой позиции, в своем восприятии транслирует все ответные транзакции, к какому бы типу они ни принадлежали в действительности, исключительно в два типа: "Ребенок послушный->Родитель критикующий" и "ребенок непослушный-> родитель критикующий". Второй тип вызывает отторжение. Так, данный абзац оформлен в нейтральном стиле,
Я просто вынужден Вам сказать, что в Вас пропадает талантливый психолог. Попытайтесь трудоустроиться в психдиспансер. Там Вас обеспечат лицензией на занятие психиатрией, и после этого Вы сможете практиковать. А без лицензии — не надо.
PS. Шутки шутками, а вовремя пролечиться или оформить инвалидность — это лучше, чем помереть от безденежья.
R>>>void f( size_t n )
R>>>{
R>>> std::auto_ptr<char> a (new char[n]);
R>>> std::auto_ptr<char> b (new char[n]);
R>>> g( a.get(), b.get(), n );
R>>>}
I>
S>>Тут UB. I>Почему?
Потому что auto_ptr будет удалять память delete'ом без скобок.
_>чтобы испортить указатели ф-я не должна стрелять в стек, она должна всего лишь вызвать для них delete и лечится это передачей константных указателей
Вообще-то, никто не мешает вызвать delete для константных указателей.
Здравствуйте, любой, Вы писали:
Л>Тут по существу уже сказали. А меня в таком коде всегда прикалывает, почему два блока нельзя за раз выделить:
Л>char *a = new char[n*2]; Л>char *b = a + n; Л>g( a, b, n ); Л>delete [] a; несчастный_труженик_проекта>delete [] b;
Здравствуйте, Кодт, Вы писали:
К>auto_ptr и boost — это как раз и есть воплощение KISS, в отличие от мосвелосипедстроймонтажа.
Каки бусты/stl в 3-х строчках кода, а? Надеюсь, ты не из тех кто начинает использовать стратегии, где достаточно православного switch?
Здравствуйте, sergey_cheban, Вы писали:
_>Надеюсь, Вы чувствуете границу между защитой чести и достоинства и самоутверждением. Я эту границу переходить не хочу.
Что не помешало Вам перейти границу "публично сраться с кандидатом после собеседования".
Зачем, спрашивается? Тем более, что, [имхо], рассказывать всем как вёл себя кандидат на собеседовании, — не принято.
Здравствуйте, sergey_cheban, Вы писали:
M>>В общем, как я уже написал, на практике без дополнительных не видно смысла что-то с этим делать, тест, строго говоря, некорректный. _>А представьте, каково нам. Мы решаем задачу, для которой математически строго доказана невозможность корректного решения: определяем, является ли файл вирусом. Ничего, как-то справляемся.
Втом то и дело, что "както". Т.е. коекак, с довольно фиговым результатом. Качество современных антивирусов оставляет желать лучшего.
Здравствуйте, sergey_cheban, Вы писали:
_>Приветствую героев, дочитавших эту тему до шестнадцатой страницы.
Извините, что влезаю, но ваш тон сообщений и отношение к кандидатам очень сильно отталкивает. Чисто по-человечески, я бы даже как с коллегой не смог бы с вами работать, но, как я понимаю, вы еще и тимлид или что-то в таком роде — это еще хуже. Я, конечно, не знаю, что там у вас на рынке труда сиплюсплюсников, может их в свободной "продаже" как собак нерезанных(во что я очень плохо верю), но любой нормальный джавист(да и вообще любой из моих знакомых) при таком отношении к нему как к кандидату бежал бы как от огня(я уже не говорю про тестовое задание типа "хочу того, не скажу чего", но опять же, может для плюсов с их вагоном средств для прострела ноги это норма, я хз). Ну, разве что у вас зп в два раза выше рынка — некоторые бы повелись. Но я бы посоветовал вам все-таки спуститься с небес немного.
Новости очень смешные. Зря вы не смотрите. Как будто за наркоманами подсматриваешь. Только тетка с погодой в завязке.
There is no such thing as a winnable war.
Здравствуйте, amberovsky, Вы писали:
A>Здравствуйте. A>При попытке устроится в одну фирму прислали "удалённое" тестовое задание.
A>2. В аргументе командной строки может встретиться символ % или \, что будет расценено printf как специальный символ.
Про обратный слэш \ — не правда. Эти вещи обрабатываются компилятором. Если из командной строки передать обратный слэш, то он так и выведется, т.е. аргумент "\n" не приведёт к переводу командной строки, т.к. будет эквивалентен "\\n" с точки зрения компилятора.
A>1.
Подозрительно как-то — создаются два массива, передаются в функцию без какой-либо инициализации, и после того как она отработает просто прибиваются, опять-таки без использования значений в массивах. Вопрос — зачем всё это?
К>А вот метнуть исключение функция g() может запросто.
А вот это маловероятно. Сигнатура функции предполагает, что она написана не на С++, а на С. А значит, скорее всего, никаких исключений она бросить не может.
И по большому счету, я вижу реальную проблему только в printf.
K>void g( const char *, const char *, size_t ); // константности в исходной задаче не было...
K>void f( size_t n )
K>{
K> сhar *a = NULL;
K> char *b = NULL;
K> if (n <= 0) // усложнение номер раз: size_t беззнаковое
K> return; // неоправданность: а может, g() должен выполняться и при нулевой длине?
K> try
K> {
K> a = new char[n]; // для справки: new char[0] возвращает ненулевой указатель на пустой массив.
K> b = new char[n];
K> g( a, b, n );
K> }
K> catch (...)
K> {
K> if (a) delete [] a; // усложнение номер два: delete[] и для нулевого указателя работает корректно
K> if (b) delete [] b; // номер два-бис
// забыл сделать throw; - сожрал исключение (может быть, оно кому-то пригодилось бы выше?)
K> }
K> if (a) delete [] a; // бабах! повторное удаление (если первое было в catch(...))
K> if (b) delete [] b; // бабах-бис
K>}
K>
K>какие авто_птр?? какой буст??? Зачем усложнять жизнь в тривиальном куске кода? KISS!
auto_ptr и boost — это как раз и есть воплощение KISS, в отличие от мосвелосипедстроймонтажа.
K>Не смущает, что нумерация массива начинается с 0, и в argc[0] == 'имя исполняемого файла'? K>Т.е. argc всегда > 0
Не смущает, что условие — if(argc>1), т.е. if(argc>=2), т.е. argv[1] валиден?
К>Да щас! К>Во-первых, сишная функция может вызвать сиплюсплюсную, и исключение полетит оттуда. И прекрасно перелетит через сишный слой.
Это правда. Об этом не подумал.
К>Во-вторых, мало ли у кого какие олдскульные замашки остались.
Ну нет. Уж если человек не пользуется векторами, то исключений он, скорее всего, тоже не признает.
К>В-третьих, сишная функция может метнуть структурное исключение (windows-specific). Аварийный размотчик стека готов и к таким испытаниям.
А может, это под Unix все работает? И там не SEH, а SIGBUS?
К>Проблема в том, что задание провалено.
Я не думаю, что это проблема . Работать в организации, предлагающей такие задания как дистанционное — верный путь в никуда. Очевидно, что задание предполагает тщательное обсуждение с кандидатом после ЛЮБОГО его ответа.
Здравствуйте, Kernan, Вы писали:
К>>Возможно, что g() способно работать и с набором параметров g(NULL,NULL,0). Этого из начальных условий задачи мы не знаем. Поэтому я и предложил трюк с max(n,1). К>>Тем более, что нагрузка на кучу для new char[0] и new char[1] (внутри vector) — почти одинаковая. K>Получается, что ты предполагаешь, что функция g проверяет n >= 0, чего может и не быть. Более того, неизвестный код может длелать какие-то действия в случае (NULL, NULL, 0).
Я просто предложил наиболее консервативный рефакторинг.
А именно, обеспечил безопасность от исключений на ввереном мне участке, никак не затронув поведение остального кода (ни вызывающего, ни внутри g()).
Единственный упрёк в сторону векторов — то, что чуть-чуть изменяется рисунок стека и кучи. Это может сыграть в условиях стресс-тестирования.
boost::scoped_array — более предпочтительный вариант в этом плане.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, alexander_st, Вы писали:
V>>>Вообще-то, никто не мешает вызвать delete для константных указателей. _>>Уп-с ... правда? _>>давно последний раз пробовали?
К>Да вот только что: http://codepad.org/9DrfRKDV
Из каких соображений тогда ну скажем ф-я std::string c_str() возвращает const char* а не просто char* ? может для того , что бы показать, что удалять этот буфер не надо?
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>
Не вижу ни малейшей проблемы в этом примере. Точнее сказать так — все зависит от контекста применения. А контекста нету. Бывают, например, случаи, когда исключения вообще запрещены. И если new может венуть 0, а в g() это обрабатывается, то даный код полностью легален. Например, я вообще вынужден пользоваться longjmp, когда память кончается — А что поделаешь на Sony PS3 Cell? — исключения сразу раздувают код процентов на 30, а там — каждый килобайт на счету. Ну и гадость же этот cell оказался, сплошной рак головы. Ждем интел лараби с его кэш-когерентностью (если дождемся, конечно).
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, sergey_cheban, Вы писали:
_>Вот этот фрагмент особенно порадовал: _>
_>void GFuncCaller_NOSTL::clearBuff()
_>{
_> if(!BufferA) delete[] BufferA;// Это не защита. Если BufferA==NULL, проблем с deleteA все равно нет
_> BufferA = NULL; // просто незачем вызывать delete
_> if(!BufferB) delete[] BufferB;
_> BufferB = NULL;
_> gBufferSize = 0;
_>};
_>
_>У Вас действительно есть свой стиль написания кода. Вы только с названием стиля ошиблись: это не Industrial, а Trash.
Блин... вызов delete как раз только при NULL... Ляп, однако. Конструкция "если указатель нулл, то сделать что-то" в пальцы въелась, однако...
Хотел напомнить, что удаление на указателе с NULL — НЕ вызывает exception, а перехват исключений можно делать не на атомарном уровне...
Но удивлен, что супер-пупер спецы из доктора веба нашли время залезть, да скачать... Вы же говорили, что код такого ничтожества, как я, вас не интересует и задание, которое я написал для другой конторы, нефиг вам присылать?
Господин гуру, а своим кодом нас, сирых и убогих недоучек, вы не могли бы порадовать? Понятное дело, баловством каким вашим собственным, а не кодом под копирайтом — да только не задачки мелкие, которые тяп-ляп да на скорую руку, а что-нибудь хотя бы отдаленно похожее на то, как вы крупные приложения дизайните... Показали бы класс. А то все критика да критика... Так критиковать чужой код — конечно, оно полегче будет, чем свой написать. Вы вообще хоть что-нибудь пишете?
А то мы, сирые да убогие, до сих пор почитаем всяких там Крэгов Ларманов да Мартинсов со всякими "бандами четырех" и т д — а ведь пишем-то всякий там трэш поганый... Ну а вы, такой новатор и знаток — ничего своего не показываете, критиковать вас не за что.
Разве что на пресловутом собеседовании, когда я вам объяснял архитектуру приложения, которое вычищал от ошибок на предыдущей работе — ляпнули,
что АСИНХРОННУЮ обработку АСИНХРОННЫХ запросов из МНОЖЕСТВЕННЫХ источников, причем с несовпадением порядка ответов с порядком запросов,
лучше делать в один поток... Дескать, процессор на сервак современный — и вперед... Причем объяснять, вам же, что вы можете иметь в виду, пришлось мне же, рисуя примитивную схему на бумаге, так как названную мной комбинацию шаблонов вы "не расслышали".
Кстати, в ваших словах про локаль — одна ошибка и, что для вас типично, масса неопределенности. Раз такие крутые — попробуйте выяснить сами.
Из вашего письма я сделал два вывода:
1. Я был абсолютно прав в своих оценках. Себя перечитайте... "трэш"...
2. Приход даже на такое собеседование — полезен. Для работодателя — вы добрались-таки хотя бы до википедии, чтобы взглянуть на выдумки ретроградов.
Еще ограничения и перечень проблем посмотрите — и вопрос "почему так не делают, хотя теоретически возможно" с асинхронной обработкой прояснится еще больше. К тому же вы отслеживаете сообщения на этой ветке — значит, что-то полезное надеетесь углядеть. Ну и по мелочам — к примеру, не задумывались, что конструктор исключений с о строкой в параметре — это "майкрософт специфик".
Для кандидата — не побывай я у вас, или веди вы себя прилично, не полез бы в интернет, чтобы поковыряться в вопросах, которые я посчитал откровенно дурными. Не нашел бы эту статью. И не увидел бы упоминания про "Эффективное использование STL", не прочитал, наконец, эту книгу и не добрался бы и до Александреску.
Здравствуйте, sergey_cheban, Вы писали:
_>Здравствуйте, Константин.
KC>>В общем, побывал я в DrWeb на собеседовании и даю правильный с их точки зрения ответ: KC>>По первому заданию: _>Я рад, что обсуждение недостатков Вашего первоначального ответа пошло Вам на пользу. И всё же: _>1. Хочу предостеречь от использования &a[0] без проверки на наличие в векторе хоть каких-то данных. Если мы не предупредили Вас об этой проблеме на собеседовании, это не значит, что её не существует.
Этот пункт вашей любезной критики, о глубокоуважаемый эксперт, тоже заслуживает незначительных сомнений со стороны недостойного меня.
Позвольте полюбопытствовать: а в "докторе вебе" принято КАЖДЫЙ подобный вызов просто на всякий случай заворачивать в контроль передаваемых параметров? Даже несмотря на то, что обязанность вызывающей стороны — предоставить корректные параметры вызываемой функции? А потом, при изменении потребностей вызываемой функции в минимальном размере буфера, делать изменения по всему тексту? Понимаю, что древний принцип распределения полномочий (его вариант в ООД зовется "Information Expert") уже признан ретроградным, и не с моим скудоумием судить о передовых подходах, но все же: функция g обязана знать свои минимальные потребности и сама проверять входные параметры. Для этого ей n и передается. Если же ей передано n, а размещено меньше, так как программ без ошибок не бывает вообще... Я так понимаю, что процессы многоуровневого тестирования тоже устарели и по вашим расчетам все-таки гораздо дешевле встраивать проверки вообще везде, где только можно. И, кстати, код, который вы забыли процитировать, переоценив желание читателей скачивать файлы и разбираться в них:
void g(char* a, char* b, size_t n )
{
// Функция на C.
// Разместить что-нибудь в буферы для использования
// вызывающей функцией - традиционный способ
// Для контроля над правильностью размещения
// используется n. В данном случае - строка либо образается(но 0 не ставится),
// либо дополняется нулями
if(!a || !b || !n)
return; // на самом деле нормально написанная функция на C должна возвращать
// именованую константу,
strncpy(a, "one two three",n);
a[n-1]='\0'; //
strncpy(b, "three two one",n);
b[n-1]='\0'; //
// сделать что-нибудь еще
printf("\n ===== func g said: Hello, I am C-styled legacy function . =============");
printf("\n ===== func g said: Under my responsibility is not only placing something to buffers. =====");
printf("\n ====== func g said: I am also says: 'Hello, word!' ===================");
return;
};
В общем, о дорогой Гуру, ко всем вашим замечаниям можно прицепиться. С учетом Вашего тона Вам следовало бы предпринять некоторые меры, чтобы о Вас не говорили: "Чукча не Писатель, чукча КРИТИК". КРИТИК И ПИСАТЕЛЬ — разные профессии. ВЫ ТОЛЬКО КРИТИК? Еще раз повторюсь: вы так многое видите в ЧУЖОМ коде и в ЧУЖИХ подходах и решениях. Покажите нам, КАК ВЫ САМИ пишете серьезные приложения — отнюдь не тексты под копирайтом,
о нет! И никто вас не просит выложить нечто, что требует больше нескольких часов работы. Но хочется увидеть нечто такое, что позволит судить о Вашем подходе к разработке КРУПНЫХ комплексов, которые потом в процессе КОЛЛЕКТИВНОЙ разработки будут дописываться и расширяться, модифицироваться и т д — очень-очень долго. Конечно, эти подходы сильно отличаются от того, что пишется на олимпиадах или для себя. Или нет? Пожалуйста, покажите нам ВАШЕ ВЕРНОЕ "КАК НАДО" конкретно, а не расплывчато и намеками, оставляя место для фантазий . Не стоит просто указывать на массу "КАК НЕ НАДО", если вы не только критик. Дайте же нам идеал, к которому нам надлежит стремиться!
Вы же сами критики не боитесь?
_>PS. Шутки шутками, а вовремя пролечиться или оформить инвалидность — это лучше, чем помереть от безденежья.
Ну, Сергей, раз в дело пошли прямые оскорбления — разговор пора заканчивать. Мир?
В принципе жаль, что ситуация сложилась с самого начала именно так, а не иначе.
Конечно, я и ваша компания несовместимы — ну и что? С парой моих несостоявшихся работодателей, к примеру,
даже нашлись нашлись другие взаимные интересы, никак не связанные с моим прямым наймом в их компании.
Давайте договоримся: больше никаких сообщений друг о друге НИГДЕ и НИКОГДА.
Разве что взаимные извинения c СОГЛАСОВАННЫМИ друг с другом текстами.
Мое мыло для вас секретом не является, вашего личного я не знаю.
Через "мой круг" я вам отправил пару сообщений.
Также я вам оставил сообщение в вашем личном журнале.
Прочитайте их, потом побеседуем НЕ на форуме.
Можем даже просто по телефону — благо, мой сотик для вас — тоже не секрет.
Договорились? И, пожалуйста, воздержитесь от ЛЮБЫХ несогласованных ответов относительно моей персоны.
Ну и я воздержусь от несогласованных комментариев о вас лично и о вашей компании.
Закончим уже, OK?
Приветствую героев, дочитавших эту тему до шестнадцатой страницы.
Несколько комментариев от автора вопросов:
1. Поскольку вопросы до сих пор используются для предварительного отбора кандидатов, дать "правильные ответы" или указать на конкретные ошибки я, увы, не могу.
2. Единственно верных ответов на эти вопросы не существует. На шестнадцати страницах обсуждения есть несколько вариантов, которые нас вполне устроили бы. Есть и такие, которые нам не нравятся.
3. Фактически, на каждый из вопросов нас устраивает любой ответ, который в каком-нибудь плане лучше предложенных фрагментов кода, и при этом не создаёт больше проблем, чем решает.
4. К сожалению, нам очень часто приходилось сталкиваться с ситуацией, когда явная глупость, написанная кандидатом, обозначала именно саму себя. Мы оцениваем то, что написано в ответе, не приписывая кандидатам свой взгляд на мир.
5. Большинство хороших программистов трудоустроены. 99% не трудоустроенных нам не подходят (прежде всего речь идёт о людях, у которых за спиной — один семестр "основ программирования на C/C++" и больше ничего). Мы хотим отфильтровывать 90% кандидатов на уровне предварительного отбора, и 90% оставшихся — на уровне очного собеседования. Это очень жёсткие фильтры, и не удивительно, что они иногда отбрасывают хороших разработчиков. Ничего страшного, такие разработчики найдут работу где-нибудь ещё.
6. Мы хотим увидеть хорошие ответы на оба вопроса.
7. Мы, увы, не можем задавать более сложные вопросы: хорошие разработчики на них не отвечают, а плохие нам не нужны.
8. Учитесь и развивайтесь, но не ломайте себя. Не пытайтесь подогнать ответ под то, что мы, якобы, хотим услышать. Руководствуйтесь своим здравым смыслом, своим чувством прекрасного, и вы найдёте команду единомышленников. Может быть, это будет наша команда. Но даже если нет, — может, оно и к лучшему?
Здравствуйте, sergey_cheban, Вы писали:
_>3. Фактически, на каждый из вопросов нас устраивает любой ответ, который в каком-нибудь плане лучше предложенных фрагментов кода, и при этом не создаёт больше проблем, чем решает.
Очень многие такие тесты на собеседовании и этот в том числе имеют серьезный недостаток: они даны вне контекста и испытуемому на самом деле предлагается усовершенствовать "сферический код в вакууме." Я бы такой код без дополнительных условий вообще переписывать не стал бы Работает — и хорошо.
Как можно говорить, что усовершенствованный код должен решать больше проблем, чем создает, если даже достоверно неясно какого рода проблемы имели место?
А то может первый код вообще хитрый хак чего-то или оптимизирован по быстродействию, а тут уже на форуме напредлагали и boost и смартпоинтеры, которые там возможно уместны как седло у коровы.
По второму коду можно сказать, что вообще-то есть библиотечные функции для разбора опций командной строки getopt для Unix или более универсально boost::program_options, а может этого не нужно совсем и тут важно, что внещний результат работы программы может быть одинаковым в случае отсутствия опций и в случае неотображаемого символа в аргументах.
Здравствуйте, igna, Вы писали:
I>Здравствуйте, Smal, Вы писали:
I>
R>>>void f( size_t n )
R>>>{
R>>> std::auto_ptr<char> a (new char[n]);
R>>> std::auto_ptr<char> b (new char[n]);
R>>> g( a.get(), b.get(), n );
R>>>}
I>
S>>Тут UB.
I>Почему?
Потому что auto_ptr::~auto_ptr вызывает delete, а не delete [].
Вызов непарного "delete-expression" == UB :
5.3.5 Delete [expr.delete]
1.
The delete-expression operator destroys a most derived object (1.8) or array created by a new-expression.
The first alternative is for non-array objects, and the second is for arrays. Whenever the delete keyword is immediately
followed by empty square brackets, it shall be interpreted as the second alternative.72) The operand shall have a pointer
type, or a class type having a single non-explicit conversion function (12.3.2) to a pointer type. The result has type void.
2.
If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion
function, and the converted operand is used in place of the original operand for the remainder of this section. In either
alternative, the value of the operand of delete may be a null pointer value. If it is not a null pointer value, in the first
alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a
subobject (1.8) representing a base class of such an object (clause 10). If not, the behavior is undefined. In the second
alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous
array new-expression.73) If not, the behavior is undefined. [ Note: this means that the syntax of the delete-expression
must match the type of the object allocated by new, not the syntax of the new-expression. —end note ] [ Note: a pointer
to a const type can be the operand of a delete-expression; it is not necessary to cast away the constness (5.2.11) of the
pointer expression before it is used as the operand of the delete-expression. —end note ]
...
R>>> std::auto_ptr<char> a (new char[n]);
R>>> std::auto_ptr<char> b (new char[n]);
I>
S>>Тут UB. I>Почему?
Потому что delete new[]. Непарная операция.
Хотя для POD-типов и обычных менеджеров памяти (не изысканных-навороченных-специфических) это сведётся к free(malloc()).
Здравствуйте, amberovsky, Вы писали:
A>[ccode] A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать? A>1. A>void g( char *, char *, size_t ); A>void f( size_t n ) A>{ A> char *a = new char[n]; A> char *b = new char[n]; A> g( a, b, n ); A> delete [] b; A> delete [] a; A>}
Проблема в том, что есть функция с ничего не говорящим именем g, для которой нет не то что комментария чего она делает, но даже названий параметров.
A_V>Теоретически, в g() могли переставить указатели на что-нибудь другое, и не удалить то, на что они указывали.
Не могли там такого сделать. Внутри этой функции она бы переставила свои локальные копии указателей, а не исходные. Чтоб была возможность переставить исходные указатели, описание функции должно быть таким:
void g( char * &, char * &, size_t );
Здравствуйте, alexander_st, Вы писали:
_>Из каких соображений тогда ну скажем ф-я std::string c_str() возвращает const char* а не просто char* ? может для того , что бы показать, что удалять этот буфер не надо?
Знаешь, есть дофига способов abuse. Например, получить указатель на неконстантный объект в стеке или статике — и попробовать его удалить. И никто не пикнет, кроме менеджера кучи перед смертью.
Ещё можно попробовать удалить delete[] &vec.front() — вполне себе неконстантный массив, рамещённый на куче... Ты просто украдёшь право владения, которое было у вектора.
Язык С++ в явном виде не декларирует права владения указуемыми объектами. Иначе бы указатель на стековый объект и указатель на объект, созданный на куче, должны были быть разного типа!!!
Это можно только гигиеной и дисциплиной обеспечить. Гигиена — это, как минимум, умными указателями с политиками.
Беда в том, что голый указатель предоставляет сразу все права (ну только с константностью есть ограничение на изменение объекта). А следовательно, всегда остаётся возможность для нецелевого использования. А следовательно — без дисциплины никуда.
Здравствуйте, KhConstantine, Вы писали:
KC>Блин... вызов delete как раз только при NULL... Ляп, однако. Конструкция "если указатель нулл, то сделать что-то" в пальцы въелась, однако...
И таких ляпов у Вас — 100% осмысленного кода. Вот ещё один:
Чем кончится вызов oSubSystem.execute(NULL) — посмотрите сами.
А вот другой: класс SomeSubSystem имеет виртуальные функции, но при этом не имеет ни предков, ни наследников. Какая муза вас посетила и почему она ушла раньше времени — загадка.
KC> Хотел напомнить, что удаление на указателе с NULL — НЕ вызывает exception, а перехват исключений можно делать не на атомарном уровне... Кому Вы хотели это напомнить? Мне? Спасибо, я уже в курсе.
KC>Но удивлен, что супер-пупер спецы из доктора веба нашли время залезть, да скачать... Вы же говорили, что код такого ничтожества, как я, вас не интересует и задание, которое я написал для другой конторы, нефиг вам присылать?
Но ведь сейчас речь идёт о другом коде — о проекте, созданном 1 мая (это дата stdafx.cpp. Cобеседование, напомню, состоялось 29 апреля), в котором упоминается торговая марка моей компании. Как тут удержаться от любопытства?
KC>Господин гуру, а своим кодом нас, сирых и убогих недоучек, вы не могли бы порадовать?
Вы меня точно ни с кем не путаете? У меня нет больших объёмов собственного кода под свободными лицензиями. Моё участие в open source разработке ограничивается вот этим скромным вкладом: http://groups.google.com/group/fardev/browse_thread/thread/94ead2b8004314e4?pli=1
KC>Вы вообще хоть что-нибудь пишете?
Да, пишу. Нет, не покажу.
KC>Разве что на пресловутом собеседовании, когда я вам объяснял архитектуру приложения, которое вычищал от ошибок на предыдущей работе — ляпнули, KC>что АСИНХРОННУЮ обработку АСИНХРОННЫХ запросов из МНОЖЕСТВЕННЫХ источников, причем с несовпадением порядка ответов с порядком запросов, KC>лучше делать в один поток...
Уж не знаю, правильно ли я понял Вас, но Вы меня в тот момент точно поняли неправильно. По сути, в ответ на Ваше утверждение "поскольку у нас есть много клиентов, нам необходимо много потоков" я предложил Вам однопоточный вариант. Преимущества у этого варианта есть, недостатки тоже, и разговор мог бы получиться интересным. Но — не судьба.
KC>Кстати, в ваших словах про локаль — одна ошибка и, что для вас типично, масса неопределенности. Раз такие крутые — попробуйте выяснить сами.
Угу, есть. Тогда тем более не понимаю, зачем Вы приписали нам свои заслуги.
Здравствуйте, Michael7, Вы писали:
M>Здравствуйте, artkarma, Вы писали:
A>>Ну раз это доктор Вэб, то A>>1. Я бы ответил что в функции f n не проверяеться размер n ,можно запросить столько памяти что не сможет выделить система
M>А если задача работать быстро, размер n заведомо не превосходит доступной памяти, а лишние проверки только затормозят?
A>>2. Стопудово что размер argv[1] не проверяеться, программа становиться уязвимой
M>Тогда еще надо проверять и все возможные модификаторы в строке ввода, вообще есть готовые функции для разбора опций, чтобы велосипед не изобретать. А тут может быть программа — это затычка для какого-нибудь скрипта и говорить про уязвимость бессмысленно.
M>В общем, как я уже написал, на практике без дополнительных не видно смысла что-то с этим делать, тест, строго говоря, некорректный.
даже не знаю...
Но данные задания, позволяют посмотреть как думает человек, обычно задание дается, и просится озвучивать что видит, что нужно изменить, и т.д.
Не обязательно правильно ответить, правильных ответов обычно нету, имеется ввиду один правильный ответ и других нет, ответов может быть много, и каждый по своему правильный, но по размышлениям примерно видно что за инженер, обычно цель не проверить знания, а проверить способность мыслить, способность видеть потенциальные ошибки, и т.д. Для инженера самое главное не помнить названия всех функций,и что они делают, а умение мыслить, а знания приобретаются по ходу работу, если чего-то инженер не знает, он должен знать где найти ответ, взять книгу или банально гуглонуть, найти ответ и применить его на практике. Зачастую студент без опыта работы но думающий, потенциально принесет больше пользы, чем просто ходячий справочник с опытом работы, а таких хватает, на собеседовании все отвечает, а на деле с простой задачей, где нужно именно думать и искать решение не может справиться.
Это мое мнение ))
_>а если эта ф-я экспортируется из dll она бы тоже принимала 2 вектора?
Дело не в экспорте, а в языке. Зачем С++ функции принимать указатели, если можно векторы?
_>а исключение по выделению памяти или по какому нить доступу по кривому указателю она тоже не может кинуть?
С++ исключение, если она написана на С — не может.
Здравствуйте, Vamp, Вы писали:
К>>А вот метнуть исключение функция g() может запросто. V>А вот это маловероятно. Сигнатура функции предполагает, что она написана не на С++, а на С. А значит, скорее всего, никаких исключений она бросить не может.
Да щас!
Во-первых, сишная функция может вызвать сиплюсплюсную, и исключение полетит оттуда. И прекрасно перелетит через сишный слой.
Во-вторых, мало ли у кого какие олдскульные замашки остались.
В-третьих, сишная функция может метнуть структурное исключение (windows-specific). Аварийный размотчик стека готов и к таким испытаниям.
Вот если бы функция была объявлена void g(char*,char*,size_t)throw() — это другое дело... Но она же не объявлена.
V>И по большому счету, я вижу реальную проблему только в printf.
Проблема в том, что задание провалено. То ли пастор Шлаг замечтался, вдыхая воздух свободы, то ли папаша Мюллер был чересчур внимателен... В пересказе топикстартера не хватает подробностей, чтобы понять, что там на самом деле.
Здравствуйте, Smal, Вы писали:
S>Здравствуйте, igna, Вы писали:
I>>Здравствуйте, Kernan, Вы писали:
I>>
K>>> catch (...)
K>>> {
K>>> if (a) delete [] a;
K>>> if (b) delete [] b;
K>>> }
K>>> if (a) delete [] a;
K>>> if (b) delete [] b;
I>>
I>>Здесь дублируются оба delete, такой код трудно сопровождать.
S>И if-ы лишние. delete отлично удаляет нулевой указатель.
не все компиляторы одинаково полезны.
Теоретически, в g() могли переставить указатели на что-нибудь другое, и не удалить то, на что они указывали. Лечится с помощью void g( char * const, char * const, size_t ); Хотя в реальных проектах пока ни разу не встречал такой защиты.
Мафиозная диктатура это нестабильность. Если не мафиозная диктатура, то Конституция и демократия.
Здравствуйте, Kernan, Вы писали:
K>Ты не можешь гарантировать то, что delete не переопределён и работает корректно.
Ну почему же
Перегрузить можно operator delete, а поведение delete expression (о котором и идет речь) для нулевого указателея четко специфицировано стандартом, и
блин — я вообще слепой — все норм по моему — единтственно — можно труями кэтчами обложить ,
но это уже коде стайлинг
A>
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>2.
A>int main(int argc, char* argv[])
A>{
A> if( argc > 1 )
A> printf( argv[1] );
A> return 0;
A>}
A>
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Kernan, Вы писали:
K>>вот только вопрос есть, тебе на вход приходит 0, а на вход функции g ты передёшь 1. Подмена понятий, однако. Хотя код неплох, есть чему поучится
К>Если ты присмотришься внимательно, то заметишь: на вход g приходит 0.
К>Но в конструкторы векторов действительно приходит 1. Это нужно затем, чтобы эмулировать поведение new char[0]. К>Дело в том, что char* p = new char[0] — это валидный ненулевой неразыменовываемый (обрати внимание на эти три слова!) указатель за конец массива.
К>Вектор же не оперирует указателями. Он отдаёт итераторы, разыменовывая которые, получаем ссылки (*v.begin()); или же сразу ссылки (v[0], v.front()). К>Потом ссылку можно восстановить до указателя (&v.front()) и, прибавив размер, получить указатель за конец массива (&v.front()+v.size()). К>Итератор конца (v.end()) неразыменовываемый, *v.end() — UB. Так что лобовое решение &*v.end() не подходит.
К>Соответственно, если вектор нулевого размера, v.begin()==v.end(), получить указатель не получится.
К>Можно, конечно, набраться наглости и сказать v.resize(0); char*p=&v.front(); — но, повторю, это неопределённое поведение. Например, в отладочной версии STL ест шанс схлопотать assert(false).
К>Но даже если не будет assertion fault, можем получить p==NULL (раз размер нулевой, то внутренний массив не выделен, а внутренние указатели выставлены в некое дефолтное, т.е. нулевое значение). К>То есть, указатель для g() будет валидный, неразыменовываемый (а мы и не собирались разыменовывать — n==0), но — нулевой.
К>Возможно, что g() способно работать и с набором параметров g(NULL,NULL,0). Этого из начальных условий задачи мы не знаем. Поэтому я и предложил трюк с max(n,1). К>Тем более, что нагрузка на кучу для new char[0] и new char[1] (внутри vector) — почти одинаковая.
Получается, что ты предполагаешь, что функция g проверяет n >= 0, чего может и не быть. Более того, неизвестный код может длелать какие-то действия в случае (NULL, NULL, 0).
Здравствуйте, carpenter, Вы писали:
C>блин — я вообще слепой — все норм по моему — единтственно — можно труями кэтчами обложить , C>но это уже коде стайлинг
Угу, слепой Среди 70 ответов выше были разные мнения и советы, и про "не всё норм", и про "можно без try-catch"...
в g(NULL,NULL,0). Этого из начальных условий задачи мы не знаем. Поэтому я и предложил трюк с max(n,1). К>>Тем более, что нагрузка на кучу для new char[0] и new char[1] (внутри vector) — почти одинаковая. K>Получается, что ты предполагаешь, что функция g проверяет n >= 0, чего может и не быть.
Не понял зачем ей это проверять. n всегда >= 0. size_t беззнаковый.
Здравствуйте, Vamp, Вы писали:
_>>а если эта ф-я экспортируется из dll она бы тоже принимала 2 вектора? V>Дело не в экспорте, а в языке. Зачем С++ функции принимать указатели, если можно векторы?
_>>а исключение по выделению памяти или по какому нить доступу по кривому указателю она тоже не может кинуть? V>С++ исключение, если она написана на С — не может.
Здравствуйте, любой, Вы писали:
Л>Здравствуйте, amberovsky, Вы писали:
Л>Тут по существу уже сказали. А меня в таком коде всегда прикалывает, почему два блока нельзя за раз выделить:
Л>char *a = new char[n*2]; Л>char *b = a + n; Л>g( a, b, n ); Л>delete [] a;
Насколько я понимаю в этом случае нужен будет в два раза больший непрерывный блок памяти, который найти меньше шансов, чем два поменьше
Здравствуйте, Tesh, Вы писали:
Л>>char *a = new char[n*2]; Л>>char *b = a + n; Л>>g( a, b, n ); Л>>delete [] a;
T>Насколько я понимаю в этом случае нужен будет в два раза больший непрерывный блок памяти, который найти меньше шансов, чем два поменьше
Зато в сумме потребуется чуть меньше памяти, т.к. накладные расходы на один блок меньше.
Советую забить У меня тоже бывали случаи, когда после собеседования хотелось кричать и махать кулаками, мне казалось что со мной поступили не справедливо, я тоже писал на рсдн например здесь http://www.rsdn.ru/forum/philosophy/2971148.aspx
Теперь я по другому к этому отношусь. Конечно собеседующие могут быть не идеальны, но эта та часть мира которую мне не изменить. Зато я могу изменить себя. Могу читать больше книжек, могу учиться разговаривать по другому, так что бы не возникало желания меня гнобить Вспоминая то собеседование, могу сказать что я был очень самоуверен, вел себя соответствующе, быстро и слишком подробно ответил на вводные вопросы про с++, мне иногда казалось, что собеседующий меня не понимает. Видимо этим его разозлил
Собеседование это ж не только проверка на знание, но и проверка насколько я коммуникабелен. Если так получилось что за время собеседование я разозлил интервьюера, то это тоже повод задуматься. Меня не возьмут на работу даже если я буду ВСЁ знать, ведь как потом со мной работать.
Вообщем я из того случая надеюсь извлёк свои уроки Вам сооветую сделать тоже самое и не на кого не злится
Здравствуйте, sergey_cheban, Вы писали:
_>Здравствуйте, amberovsky, Вы писали:
A>>При попытке устроится в одну фирму прислали "удалённое" тестовое задание. _>Раз уж это задание попало на RSDN и стало предметом обсуждения, позвольте несколько комментариев от автора.
_>1. Целью этого задания является предварительный отсев кандидатов. Если кандидат нормально ответил на вопросы, мы приглашаем его на собеседование. Как показывает практика, на этом этапе отсеивается примерно половина кандидатов, что серьёзно экономит наше время. _>2. Основным требованием к заданию является простота его выполнения (для тех кандидатов, которые нам подходят). Сложные задания отпугивают кандидатов, а нам это не нужно. _>3. Никаких "единственно правильных ответов" не существует. Ответы рассматривают разработчики, а не отдел кадров. На этом этапе мы готовы принять любой вариант, который решает больше проблем, чем создаёт. _>4. Мы тоже иногда ошибаемся, причём при наличии сомнений предпочитаем отказать кандидату. В конце концов, каждый ошибочно принятый на работу человек обходится нам в несколько десятков тысяч рублей чистого убытка, а каждый ошибочно отвергнутый — найдёт себе применение где-нибудь в другом месте. _>5. Если кандидат в процессе чтения этой ветки узнаёт много нового и интересного для себя — это верный признак того, что ему ещё есть чему поучиться до того, как он сможет претендовать на работу у нас.
>> несколько десятков тысяч рублей чистого убытка
Понятен стандартный уровень ваших зарплат. >> до того, как он сможет претендовать на работу у нас
Да, каждый сам устанавливает планку. И компании уровня "несколько десятков тысяч рублей"/брэнд из разряда "а разве они все еще существуют?" — тоже.
Ну что же, из Ваших прочих сообщений видно, что лично Ваш код проанализировать не выйдет. Жаль.Везде можно найти нечто интересное и поучительное.
В частности, здесь и сейчас даю наводку на популярные книги Эрика Берна, основателя транзакционного анализа: "Игры, в которые играют люди" и "Люди, в которые играют в игры". Форум — программистский, наводка — на работы по психологии, полезные всем, чья существенная часть работы связана с взаимодействием с другими людьми.
С другой стороны, есть такая крайне полезная техника: "инспекция кода/дизайна/документа равными". Двойной эффект: огромный плюс в плане передачи знаний + очень простой и очень дешевый, но очень эффективный способ повышения качества кода. Соответственно и производительности труда в целом (и программисты быстрее и полнее изучают систему/быстрее работают за счет снижения затрат на собственные исследования кода, и раннее обнаружение ошибок означает снижение затрат за счет сокращения цикла код->тестирование-> CR -> анализ -> исправление ). Практикуется Моторолой с ее "шесть сигм" и массой других брэндов. Заключается в обязательном объяснении разработчиком своих исправлений группе коллег. Но требует соблюдения определенных норм общения — в частности, обязательно исключительно общение в благожелательно-нейтральном/безэмоциональном стиле ("взрослый->взрослый", определение проблемы/сбор информации/поиск решения). Иначе — конфликты, и исполнители думают не столько о работе, сколько прокручивают в голове разные внутренние диалоги.
В адаптированном виде описание применения транзакционного анализа нередко включается в ученики по управлению проектами, team building, etc
В соответствии с этой терминологией: Практически все транзакции идут у Вас с позиции "родитель критикующий". Позиция известна, как наиболее конфликтная и наименее продуктивная. Человек, общающийся с этой позиции, в своем восприятии транслирует все ответные транзакции, к какому бы типу они ни принадлежали в действительности, исключительно в два типа: "Ребенок послушный->Родитель критикующий" и "ребенок непослушный-> родитель критикующий". Второй тип вызывает отторжение. Так, данный абзац оформлен в нейтральном стиле, а какие чувства он вызывает у вас? Замечу, что на собеседовании вы тоже получали с моей стороны транзакции "взрослый->взрослый".
Конфликтная природа позиции "Родитель критикующий" вызвана прежде всего тем, что для человека в позиции "родитель критикующий" единственно приемлемой ответной транзакцией является транзакция с позиции "ребенок послушный". Соответственно собеседнику навязывается позиция "ребенок". Поскольку для полноценного взрослого человека, без заниженной самооценки, позиция "ребенок послушный" является крайне невыгодной, идет отторжение невыгодного шаблона поведения на инстинктивном уровне. Если же по каким-либо причинам человек вынужден следовать шаблону "ребенок послушный" долгое время, рискует в этой позиции и застрять, заполучив в итоге заниженную самооценку. Что крайне вредно для продуктивности — человек слишком боится ошибиться, следует принципу не ошибается лишь тот, кто ничего не делает.
В общем, при коллективной работе оптимальным признается исключительно "взрослый->взрослый", ограниченно допустимым (если собеседник сам переключился в позицию "ребенок", что типично при обращении к человеку с более высоким рангом) — "родитель поддерживающий/покровительствующий".
Что интересно: человек, для которого типична позиция "родитель критикующий", зачастую всеми силами избегает что-либо делать САМ из-за инстинктивного страха совершить ошибку — слишком привык к своей позиции и слишком хорошо знает, что чувствуют его собеседники. Тем более, что этот шаблон поведения зачастую является результатом обезъянничанья со стороны человека, для которого ранее был типичен шаблон "ребенок послушный".
Если в вашей компании большая текучка и проблема с подбором кадров — возможно, это небольшое эссе укажет на одну из причин. Возможно, в схеме "может/ему разрешено/хочет" требование следовать шаблону "послушный ребенок" оказалось в "может"
Здравствуйте, sergey_cheban, Вы писали: _>5. Если кандидат в процессе чтения этой ветки узнаёт много нового и интересного для себя — это верный признак того, что ему ещё есть чему поучиться до того, как он сможет претендовать на работу у нас.
C учетом предлагаемой зарплаты, других кандидатов вы наврятле увидите усебя на собеседовании
_>> 3. Фактически, на каждый из вопросов нас устраивает любой ответ, который в каком-нибудь плане лучше предложенных фрагментов кода, и при этом не создаёт больше проблем, чем решает. M> Очень многие такие тесты на собеседовании и этот в том числе имеют серьезный недостаток: они даны вне контекста и испытуемому на самом деле предлагается усовершенствовать "сферический код в M> вакууме." Я бы такой код без дополнительных условий вообще переписывать не стал бы Работает — и хорошо.
Представьте, что вышеуказанные фрагменты кода достались Вам в наследство от программиста, уволенного за профнепригодность, и у Вас есть немного времени на рефакторинг именно этих фрагментов.
M> Как можно говорить, что усовершенствованный код должен решать больше проблем, чем создает, если даже достоверно неясно какого рода проблемы имели место?
Наличие проблем — это часть вопроса. Исправляйте те проблемы, которые видите. Считайте, что Вы — последний человек, который стоит между этими фрагментами кода и реальной эксплуатацией в какой-нибудь life-critical system.
M> А то может первый код вообще хитрый хак чего-то или оптимизирован по быстродействию, а тут уже на форуме напредлагали и boost и смартпоинтеры, которые там возможно уместны как седло у коровы. M> По второму коду можно сказать, что вообще-то есть библиотечные функции для разбора опций командной строки getopt для Unix или более универсально boost::program_options, а может этого не нужно M> совсем и тут важно, что внещний результат работы программы может быть одинаковым в случае отсутствия опций и в случае неотображаемого символа в аргументах.
Ради бога. Если Вы считаете, что от использования буста, смартпоинтеров, getopt или чёрта в ступе программа в чём-то выиграет, то пожалуйста, используйте их. Только напишите, в чём программа, по Вашему мнению, выиграла.
Здравствуйте.
При попытке устроится в одну фирму прислали "удалённое" тестовое задание.
Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
1.
void g( char *, char *, size_t );
void f( size_t n )
{
char *a = new char[n];
char *b = new char[n];
g( a, b, n );
delete [] b;
delete [] a;
}
2.
int main(int argc, char* argv[])
{
if( argc > 1 )
printf( argv[1] );
return 0;
}
Я ответил так:
1. new может не сработать, нужно добавить, например, try/catch. Кроме того, g может "испортить" указатели (как тут просто решить я не знаю)
2. В аргументе командной строки может встретиться символ % или \, что будет расценено printf как специальный символ.
Если следующие символы совпадут с какой-либо спецификацией формата (для % или \), то в данном случае для % — UB, для \ — произойдёт подстановка.
Решение — printf("%s", argv[1]);
Ещё можно проверить что printf действительно напечатало нужное количество символов.
В итоге мне отказали.
В чём я ошибся или чего важного не указал?
Здравствуйте, amberovsky, Вы писали:
A>2. В аргументе командной строки может встретиться символ % или \, что будет расценено printf как специальный символ. A>Если следующие символы совпадут с какой-либо спецификацией формата (для % или \), то в данном случае для % — UB, для \ — произойдёт подстановка. A>Решение — printf("%s", argv[1]);
Тут да, неизвестные строки всегда надо выводить как printf("%s", str).
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>2.
A>int main(int argc, char* argv[])
A>{
A> if( argc > 1 )
A> printf( argv[1] );
A> return 0;
A>}
A>
A>Я ответил так: A>1. new может не сработать, нужно добавить, например, try/catch. Кроме того, g может "испортить" указатели (как тут просто решить я не знаю)
new может кинуть исключение, g тоже может кинуть исключение, соответственно могут возникнуть утечки. В g невозможно проверить "валидность" указателей и длину блоков. Насчет второго можно возразить, что ответственность лежит на пользователе функции g, но ИМХО assert в функции не помешал бы.
Обе эти проблемы можно решить, если в качестве параметров g использовать std::vector<char>.
Не понятно, что означает фраза
g может "испортить" указатели
A>2. В аргументе командной строки может встретиться символ % или \, что будет расценено printf как специальный символ. A>Если следующие символы совпадут с какой-либо спецификацией формата (для % или \), то в данном случае для % — UB, для \ — произойдёт подстановка. A>Решение — printf("%s", argv[1]);
В общем верно.
A>В итоге мне отказали. A>В чём я ошибся или чего важного не указал?
Здравствуйте, amberovsky, Вы писали:
A>Здравствуйте. A>При попытке устроится в одну фирму прислали "удалённое" тестовое задание.
A>
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>
A>Я ответил так: A>1. new может не сработать, нужно добавить, например, try/catch. Кроме того, g может "испортить" указатели (как тут просто решить я не знаю)
ничего g не может испортить.
A>В итоге мне отказали. A>В чём я ошибся или чего важного не указал?
Здравствуйте, Smal, Вы писали:
R>>Если доступен scoped_ptr<>, то лучше использовать его.
S>Ну, тогда уж S>
S>void f( size_t n )
S>{
S> std::vector<char> a (n);
S> std::vector<char> b (n);
S> g( &a[0], &b[0], n );
S>}
S>
Насколько это естественно? По-моему это слишком сложно для обычной передачи параметров.
S>или
S>
S>void f( size_t n )
S>{
S> boost::scoped_array<char> a (new char[n]);
S> boost::scoped_array<char> b (new char[n]);
S> g( a.get(), b.get(), n );
S>}
S>
Использовать boost в таких заданиях нормально?
p.s. На второй вопрос я ответил правильно, в первом так себе. Почему же отказали?
S>>void f( size_t n )
S>>{
S>> std::vector<char> a (n);
S>> std::vector<char> b (n);
S>> g( &a[0], &b[0], n );
S>>}
S>>
A>Насколько это естественно? По-моему это слишком сложно для обычной передачи параметров.
Это не очень красивая, но совершенно стандартная идиома.
Скотт Мейерс, Эффективное использование STL, Item 16
Item 16. Know how to pass vector and string data to legacy APIs.
Since C++ was standardized in 1998, the C++ elite haven't been terribly subtle in their attempt to nudge programmers away from arrays and towards vectors. They've been similarly overt in trying to get developers to shift from char* pointers to string objects. There are good reasons for making these changes, including the elimination of common programming errors (see Item 13) and the ability to take full advantage of the power of the STL algorithms (see. e.g., Item 31).
Still, obstacles remain, and one of the most common is the existence of legacy C APIs that traffic in arrays and char* pointers instead of vector and string objects. Such APIs will exist for a long time, so we must make peace with them if we are to use the STL effectively.
Fortunately, it's easy. If you have a vector v and you need to get a pointer to the data in v that can be viewed as an array, just use &v[0]. For a string s, the corresponding incantation is simply s.c_str(). But read on. As the fine print in advertising often points out, certain restrictions apply.
Given
vector<int> v;
the expression v[0] yields a reference to the first element in the vector, so &v[0] is a pointer to that first element. The elements in a vector are constrained by the C++ Standard to be stored in contiguous memory, just like an array, so if we wish to pass v to a C API that looks something like this.
void doSomething(const int* pInts, size_t numlnts);
we can do it like this:
doSomething(&v[0], v.size());
Maybe. Probably. The only sticking point is if v is empty. If it is, v.size() is zero, and &v[0] attempts to produce a pointer to something that does not exist. Not good. Undefined results. A safer way to code the call is this:
if (!v.empty()) {
doSomething(&v[0], v.size());
}
If you travel in the wrong circles, you may run across shady characters who will tell you that you can use v.begin() in place of &v[0], because (these loathsome creatures will tell you) begin returns an iterator into the vector, and for vectors, iterators are really pointers. That's often true, but as Item 50 reveals, it's not always true, and you should never rely on it. The return type of begin is an iterator, not a pointer, and you should never use begin when you need to get a pointer to the data in a vector. If you're determined to type v.begin() for some reason, type &*v.begin(), because that will yield the same pointer as &v[0], though it's more work for you as a typist and more obscure for people trying to make sense of your code. Frankly, if you're hanging out with people who tell you to use v.begin() instead of &v[0], you need to rethink your social circle.
Здравствуйте, Vamp, Вы писали:
К>>А вот метнуть исключение функция g() может запросто. V>А вот это маловероятно. Сигнатура функции предполагает, что она написана не на С++, а на С. А значит, скорее всего, никаких исключений она бросить не может.
Здравствуйте, amberovsky, Вы писали:
A>Здравствуйте, SaZ, Вы писали:
SaZ>>Если задание удалённое — то неправильно. А на собеседовании попросили бы аргументировать. Почему — написано ниже.
A>Вы считаете ошибку со слешем причиной отказать?
Не только в слэше делать, но зачем вспоминать, если не уверен в правильности? Вспомнился случай с одного экзамена, когда преподаватель как раз подобными вопросами всех валил. А причина была простая: непонимание группой того, что \n — это один символ, понятный лишь компилятору и не принимаемый извне.
Признаю, что возможно немного погорячился с оценкой. Цепляться в этом случае или нет, так сказать, человеко-зависимо. Мои преподаватели бы зацепились (на собеседовании, а на экзамене — тем более).
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>
инициализация строк, какой нить const char* или std::string в параметрах g и то, что по хорошему надо в g передать 2 размера, а не один (если это не strcpy )..
также, как вариант ответа, сойдёт "этот код хз что делает, поэтому его надо переписать по человечески"..
Здравствуйте, Vamp, Вы писали:
SC>>Каким образом она это указывает? V>Способом передачи параметров. Если бы она была написана на С++, она бы принимала два вектора.
а если эта ф-я экспортируется из dll она бы тоже принимала 2 вектора?
а исключение по выделению памяти или по какому нить доступу по кривому указателю она тоже не может кинуть?
Здравствуйте, alexander_st, Вы писали:
V>>Вообще-то, никто не мешает вызвать delete для константных указателей. _>Уп-с ... правда? _>давно последний раз пробовали?
Здравствуйте, Vamp, Вы писали:
К>>Во-вторых, мало ли у кого какие олдскульные замашки остались. V>Ну нет. Уж если человек не пользуется векторами, то исключений он, скорее всего, тоже не признает.
Незнание законов не освобождает от ответственности. Раз есть new, значит, есть и std::bad_alloc.
Вот если на уровне main() забыли написать try-catch, ловящий все долетающие до этого уровня исключения, то внезапный бросок приведёт к terminate(), и тут, конечно, уже пофиг на какую-либо стековую гигиену.
К>>В-третьих, сишная функция может метнуть структурное исключение (windows-specific). Аварийный размотчик стека готов и к таким испытаниям. V>А может, это под Unix все работает? И там не SEH, а SIGBUS?
А кстати, как в юниксе принято раскручивать стек после сигналов?
В виндах всё несложно: каждая функция, собирающаяся раскручивать, просто на входе в блок регистрирует обработчик исключения, на выходе дерегистрирует. И задача этого обработчика — вызвать деструкторы и отпустить исключение дальше, к следующему обработчику в цепочке. Компилятор берёт на себя задачу написать такой пролог-эпилог. В никсах компилятор не столь добр к пользователю, ну так это уже его личная лень
К>>Проблема в том, что задание провалено. V>Я не думаю, что это проблема . Работать в организации, предлагающей такие задания как дистанционное — верный путь в никуда. Очевидно, что задание предполагает тщательное обсуждение с кандидатом после ЛЮБОГО его ответа.
Не буду заниматься инсинуациями, не зная подробностей
К>Незнание законов не освобождает от ответственности. Раз есть new, значит, есть и std::bad_alloc.
Где? В вызывающей функции он есть. Но я вообще-то не считаю, что bad_alloc надо перехватывать, за исключением редких стратегий типа — попросим 10 гиг, не получилось — тогда 5, опять не получилось, тогда 2 Кб. Как правило, программа просит столько памяти, сколько ей надо для работы, и ничего осмысленного после bad_alloc сделать не сможет. Более того, в ситуации тотальной нехватки вполне возможно, что даже диагностические сообщения вывести не удастся, так что пусть уж честно рушится. По крайней мере, в тех же Unix будет вполне читабельная корка.
К>А кстати, как в юниксе принято раскручивать стек после сигналов?
Никак. С другой стороны, сигналы как правило посылаются только в том случае, когда сделать уже реально ничего нельзя. Ну как ты будешь раскручивать стек, если у тебя стек порушился к чертям?
К>Не буду заниматься инсинуациями, не зная подробностей
Это же самое интересное!
Здравствуйте, Vamp, Вы писали:
K>>какие авто_птр?? какой буст??? Зачем усложнять жизнь в тривиальном куске кода? KISS! V>А ведь ты не понял принцип КИСС.
Просвяти меня.
Здравствуйте, Kernan, Вы писали:
S>>И if-ы лишние. delete отлично удаляет нулевой указатель. K>не все компиляторы одинаково полезны.
Компилятор здесь ни при чем. delete, равно как и free нулевого указателя — это законная операция, так что смысла в проверках нет, если только тебе не платят за килограммы кода
Но разу уж мы пишем в c-стиле, то надо идти до конца
void g( const char *, const char *, size_t );
void f( size_t n )
{
сhar *a = malloc(n),
*b = malloc(n);
if (a && b) g( a, b, n );
free(a);
free(b);
}
Здравствуйте, Alexey_VL, Вы писали:
A_V>Теоретически, в g() могли переставить указатели на что-нибудь другое, и не удалить то, на что они указывали. Лечится с помощью void g( char * const, char * const, size_t ); Хотя в реальных проектах пока ни разу не встречал такой защиты.
Наложение константности на данные, передаваемые по значению (собственно указатели) — большого смысла не несёт.
Всё равно наружу любые изменения не просочатся.
А внутри — элементарно снимается
void g(char* const a)
{
char* aa = a;
.......
aa = bbbb;
.......
}
Здравствуйте, andrey.desman, Вы писали:
AD>Здравствуйте, Kernan, Вы писали:
S>>>И if-ы лишние. delete отлично удаляет нулевой указатель. K>>не все компиляторы одинаково полезны.
AD>Компилятор здесь ни при чем. delete, равно как и free нулевого указателя — это законная операция, так что смысла в проверках нет, если только тебе не платят за килограммы кода
Ты не можешь гарантировать то, что delete не переопределён и работает корректно.
Здравствуйте, Кодт, Вы писали:
К>Наложение константности на данные, передаваемые по значению (собственно указатели) — большого смысла не несёт.
Это я ступил. Просто была давно подуманная мысль про char * const , что она так работает. Поскольку в реальности эта мысль не применялась и как следствие не продумывалась заново, она до сегодняшняего дня лежала в моске как истинная
Мафиозная диктатура это нестабильность. Если не мафиозная диктатура, то Конституция и демократия.
Здравствуйте, Kernan, Вы писали:
K>Ты не можешь гарантировать то, что delete не переопределён и работает корректно.
А еще я не могу гарантировать, что завтра мне кирпич на голову не упадет
Здравствуйте, Kernan, Вы писали:
K>вот только вопрос есть, тебе на вход приходит 0, а на вход функции g ты передёшь 1. Подмена понятий, однако. Хотя код неплох, есть чему поучится
Если ты присмотришься внимательно, то заметишь: на вход g приходит 0.
Но в конструкторы векторов действительно приходит 1. Это нужно затем, чтобы эмулировать поведение new char[0].
Дело в том, что char* p = new char[0] — это валидный ненулевой неразыменовываемый (обрати внимание на эти три слова!) указатель за конец массива.
Вектор же не оперирует указателями. Он отдаёт итераторы, разыменовывая которые, получаем ссылки (*v.begin()); или же сразу ссылки (v[0], v.front()).
Потом ссылку можно восстановить до указателя (&v.front()) и, прибавив размер, получить указатель за конец массива (&v.front()+v.size()).
Итератор конца (v.end()) неразыменовываемый, *v.end() — UB. Так что лобовое решение &*v.end() не подходит.
Соответственно, если вектор нулевого размера, v.begin()==v.end(), получить указатель не получится.
Можно, конечно, набраться наглости и сказать v.resize(0); char*p=&v.front(); — но, повторю, это неопределённое поведение. Например, в отладочной версии STL ест шанс схлопотать assert(false).
Но даже если не будет assertion fault, можем получить p==NULL (раз размер нулевой, то внутренний массив не выделен, а внутренние указатели выставлены в некое дефолтное, т.е. нулевое значение).
То есть, указатель для g() будет валидный, неразыменовываемый (а мы и не собирались разыменовывать — n==0), но — нулевой.
Возможно, что g() способно работать и с набором параметров g(NULL,NULL,0). Этого из начальных условий задачи мы не знаем. Поэтому я и предложил трюк с max(n,1).
Тем более, что нагрузка на кучу для new char[0] и new char[1] (внутри vector) — почти одинаковая.
Здравствуйте, Kernan, Вы писали:
K>Получается, что ты предполагаешь, что функция g проверяет n >= 0, чего может и не быть. Более того, неизвестный код может длелать какие-то действия в случае (NULL, NULL, 0).
Может, не может, а вдруг и не так, а сяк. Предположения летят в мусорное ведро. Акстись запускать вот так просто какой-то неизвестный код! Просто смотришь в код функции и видишь что она делает. Или доку читаешь. Или, наконец, просто один раз проверяешь.
Изначальный вопрос — дерьмо, т.к. искать сферические проблемы в сфрическом коде суть занятие бесполезное, потому что проблем можно напридумывать кучу. И спорить кто прав, кто виноват потом можно до усрачки, потому что у одного глаз косит на 30 градусов, а у другого на 17, и никак их точки зрения не совпадут...
А самый главный косяк в изначальном коде — это с особым усердием подобранные, как бы намекающие на всю глупость ситуации, имена функций и переменных. Вот где есть простор для фантазии! Кто какое имя придумает? Устроим знатный holy war!
Здравствуйте, Vamp, Вы писали:
R>>Если вылетит исключение при выделении памяти под b, то память для a не будет освобождена -> утечка память. V>Ну какая утечка? Если вылетит исключение в приведенном фрагменте, программа просто завершится и всю память почистит ОС.
Почему же ? Окружение, в котором вызывается функция f нам неизвестно. Вполне возможно что там исключение будет перехвачено, и программа нисколько не завершится. Итого: утечка.
Здравствуйте, maykie, Вы писали:
M>Не понял зачем ей это проверять. n всегда >= 0. size_t беззнаковый.
Почему size_t всегда должен быть беззнаковым?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, alexander_st, Вы писали:
_>Здравствуйте, Кодт, Вы писали:
К>>Здравствуйте, alexander_st, Вы писали:
V>>>>Вообще-то, никто не мешает вызвать delete для константных указателей. _>>>Уп-с ... правда? _>>>давно последний раз пробовали?
К>>Да вот только что: http://codepad.org/9DrfRKDV _>Из каких соображений тогда ну скажем ф-я std::string c_str() возвращает const char* а не просто char* ? может для того , что бы показать, что удалять этот буфер не надо?
Нет, для того, чтобы показать что этот буфер изменять не надо.
Здравствуйте, amberovsky, Вы писали:
A>В итоге мне отказали. A>В чём я ошибся или чего важного не указал?
1.
void g( char *, char *, size_t );
Функция совершенно непонятно что делает? функцию с именем g и без коментария необходимо переписать.
void f( size_t n )
Кроме описанного выше, нет контроля за удалением памяти при возникновении исключения, плюс если нет специальных требований, следует использовать безопасные массивы.
2. Без указания спецификаций непонятно о чем говорить. Использование специальных символов может быть by design, важной фичей.
Здравствуйте, Smal, Вы писали:
S>Нет, для того, чтобы показать что этот буфер изменять не надо.
из каких соображений? что будет если его изменить?.... точнее чем изменение буфера хуже его удаления?
Здравствуйте, minorlogic, Вы писали:
M>2. Без указания спецификаций непонятно о чем говорить. Использование специальных символов может быть by design, важной фичей.
При использовании %s printf будет пытаться вытаскивать из стека указатели на строки (которые туда никто не клал). Как минимум это UB, и будет печататься мусор, как максимум — в случае более сложной программы можно анализировать стек и вытаскивать какие то, возможно, важные данные.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, alexander_st, Вы писали:
_>>Из каких соображений тогда ну скажем ф-я std::string c_str() возвращает const char* а не просто char* ? может для того , что бы показать, что удалять этот буфер не надо?
К>Знаешь, есть дофига способов abuse. Например, получить указатель на неконстантный объект в стеке или статике — и попробовать его удалить. И никто не пикнет, кроме менеджера кучи перед смертью. К>Ещё можно попробовать удалить delete[] &vec.front() — вполне себе неконстантный массив, рамещённый на куче... Ты просто украдёшь право владения, которое было у вектора.
К>Язык С++ в явном виде не декларирует права владения указуемыми объектами. Иначе бы указатель на стековый объект и указатель на объект, созданный на куче, должны были быть разного типа!!! К>Это можно только гигиеной и дисциплиной обеспечить. Гигиена — это, как минимум, умными указателями с политиками. К>Беда в том, что голый указатель предоставляет сразу все права (ну только с константностью есть ограничение на изменение объекта). А следовательно, всегда остаётся возможность для нецелевого использования. А следовательно — без дисциплины никуда.
это все хорошо и правильно, но увы, даже на 1% не отвечает на мой вопрос
Здравствуйте, alexander_st, Вы писали:
_>это все хорошо и правильно, но увы, даже на 1% не отвечает на мой вопрос
Потому что ты упорно задаёшь свой вопрос как риторический.
Буквальный ответ про string: она возвращает указатель на массив из константных элементов, чтобы никто не мог изменять значения этих элементов.
При этом подразумевается, что массивом владеют только объекты string. Какое там владение: монопольное или разделяемое (когда несколько объектов пользуются общим массивом, идиома COW) — для пользователя не имеет значения. НЕ ЕГО СОБАЧЬЕ ДЕЛО.
Это подразумевание запрещает управлять памятью извне. И более того, если программист не враг себе, он не имеет права управлять никакой памятью, которая ему не отдана во владение.
Здравствуйте, Vamp, Вы писали:
К>>Проблема в том, что задание провалено. V>Я не думаю, что это проблема . Работать в организации, предлагающей такие задания как дистанционное — верный путь в никуда. Очевидно, что задание предполагает тщательное обсуждение с кандидатом после ЛЮБОГО его ответа.
Да ладно тебе. Две элементарные задачи и ни одного правильного ответа. Почему здесь нужно тщательное обсуждение? Тем более задача решается удалённо, никто никого не торопит.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Vamp, Вы писали:
К>>Незнание законов не освобождает от ответственности. Раз есть new, значит, есть и std::bad_alloc. V>Где? В вызывающей функции он есть. Но я вообще-то не считаю, что bad_alloc надо перехватывать, за исключением редких стратегий типа — попросим 10 гиг, не получилось — тогда 5, опять не получилось, тогда 2 Кб. Как правило, программа просит столько памяти, сколько ей надо для работы, и ничего осмысленного после bad_alloc сделать не сможет. Более того, в ситуации тотальной нехватки вполне возможно, что даже диагностические сообщения вывести не удастся, так что пусть уж честно рушится. По крайней мере, в тех же Unix будет вполне читабельная корка.
Похоже, ты никогда не видел переопределенного new. Который например, может брать память из какого-нибудь ограниченного по размеру блока, отображенного файла и вообще хз откуда. И даже наличие свободного гига еще совсем не значит, что malloc на гиг будет успешным — фрагментацию пока никто не отменял! Нет никакой гарантии, что ОС справится с фрагментацией!
И даже при стандартном new в ответ на bad_alloc еще много чего можно сделать. Например, если не получилось выполнить текущую задачу в списке — можно просто ее не выполнить и перейти к следующей.
Здравствуйте, McSeem2, Вы писали:
MS>Не вижу ни малейшей проблемы в этом примере. Точнее сказать так — все зависит от контекста применения. А контекста нету. Бывают, например, случаи, когда исключения вообще запрещены. И если new может венуть 0, а в g() это обрабатывается, то даный код полностью легален.
А такой new так же пишется?
Насколько можно понять, тест на знание _современного_ С++?
Здравствуйте, amberovsky, Вы писали:
A>Здравствуйте. A>При попытке устроится в одну фирму прислали "удалённое" тестовое задание.
A>
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>2.
A>int main(int argc, char* argv[])
A>{
A> if( argc > 1 )
A> printf( argv[1] );
A> return 0;
A>}
A>
A>Я ответил так: A>1. new может не сработать, нужно добавить, например, try/catch. Кроме того, g может "испортить" указатели (как тут просто решить я не знаю) A>2. В аргументе командной строки может встретиться символ % или \, что будет расценено printf как специальный символ. A>Если следующие символы совпадут с какой-либо спецификацией формата (для % или \), то в данном случае для % — UB, для \ — произойдёт подстановка. A>Решение — printf("%s", argv[1]); A>Ещё можно проверить что printf действительно напечатало нужное количество символов.
A>В итоге мне отказали. A>В чём я ошибся или чего важного не указал?
void g( char *, char *, size_t)
{
}
void f( size_t n )
{
char *a = new(nothrow) char[n];
char *b = new(nothrow) char[-n];// если не возможно выделить память, как в данном случае, не генерит эксепшн, просто возвращает 0.
Здравствуйте, amberovsky, Вы писали:
A>Здравствуйте. A>При попытке устроится в одну фирму прислали "удалённое" тестовое задание.
A>
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>2.
A>int main(int argc, char* argv[])
A>{
A> if( argc > 1 )
A> printf( argv[1] );
A> return 0;
A>}
A>
A>Я ответил так: A>1. new может не сработать, нужно добавить, например, try/catch. Кроме того, g может "испортить" указатели (как тут просто решить я не знаю) A>2. В аргументе командной строки может встретиться символ % или \, что будет расценено printf как специальный символ. A>Если следующие символы совпадут с какой-либо спецификацией формата (для % или \), то в данном случае для % — UB, для \ — произойдёт подстановка. A>Решение — printf("%s", argv[1]); A>Ещё можно проверить что printf действительно напечатало нужное количество символов.
A>В итоге мне отказали. A>В чём я ошибся или чего важного не указал?
void g( char *, char *, size_t)
{
}
void f( size_t n )
{
char *a = new(nothrow) char[n];
char *b = new(nothrow) char[-n]; // если память не возможно выделить, как в данном случае, эксепшена не будет, возвратится 0
Здравствуйте, KoriBRand, Вы писали:
KBR>Здравствуйте, SergeyT., Вы писали:
ST>>Здравствуйте, amberovsky, Вы писали:
S>>>>Ну, тогда уж S>>>>
S>>>>void f( size_t n )
S>>>>{
S>>>> std::vector<char> a (n);
S>>>> std::vector<char> b (n);
S>>>> g( &a[0], &b[0], n );
S>>>>}
S>>>>
ST>>Это не очень красивая, но совершенно стандартная идиома. ST>>Скотт Мейерс, Эффективное использование STL, Item 16
KBR>Ну нахрен такие идиомы. Добавишь ты в вектор новый элемент, сделает он реаллокацию внутреннего буфера, и указатель, который передавался в doSomething станет невалидным.
вообще-то данный пример изначально выглядел как
p = new
doSomething
delete p
если doSomething сохраняет и при повторных вызовах его использует, то будет такая же проблема
что и с vector,
в чем смысл вашего возражения?
Здравствуйте, amberovsky, Вы писали:
A>Здравствуйте. A>При попытке устроится в одну фирму прислали "удалённое" тестовое задание.
Мне пришло такое задание от DrWeb. Кто-то еще так развлекается?
В общем, побывал я в DrWeb на собеседовании и даю правильный с их точки зрения ответ:
По первому заданию:
void f_drWeb( size_t n )
{
using namespace std;
vector<char> a (n), b(n);
g( &a[0], &b[0], n );
cout<<endl<<"func f_drWeb said:"<<"a="<<&a[0]<<" b="<<&b[0];
};
По второму заданию:
int main(int argc, char* argv[])
{
const char *locId="Russian";
// Для Cif(argc>1)
{
printf("\n printf said: %s", argv[1]);
setlocale(LC_ALL, "Russian"); // "Russian" - для винды и русского. Это необходимо для C,
// но недостаточно для wcout
}
//Для потоков
locale loc(locId);
locale::global(loc); // Это необходимо для wcout(setlocale недостаточно).
wcout.imbue(loc); // Применяем к wcout, потому что wcout УЖЕ открыт. Действует и на stdout
// так что в принципе можно обойтись и без setlocaleif(argc>1)
{
wcout<<endl<<"wcout said:"<<argv[1];
};
return 0;
}
A>
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>2.
A>int main(int argc, char* argv[])
A>{
A> if( argc > 1 )
A> printf( argv[1] );
A> return 0;
A>}
A>
Кроме того, можете скачать http://piterludi.com/fordeveloponbox/stlfuns.zip
В этом проекте (VC++ 2008) я продемонстрировал также поведение виртуальных функций, вызываемых из конструкторов и деструкторов — вопрос,
который почему-то нередко возникает на собеседованиях. Также немного побаловался с промышленным дизайном для этой же задачи.
Хотя по ходу собеседования я и сделал вывод, что промышленный дизайн и общепринятые подходы они считают
ненужным усложнением и ретроградством.
Возможно, я ошибаюсь: все-таки собеседование проходило в стиле "опусти кандидата",
а при таком стиле язвительно критикуется любое высказывание.
Вероятно, предполагается, что осознавший свое ничтожество и полностью дезориентированный кандидат
преисполнится безумной радости, если ему все-таки сделают предложение поработать в такой новаторской фирме.
Просто держите в голове, что "опустить" можно любого: конкретно моих "экзаменаторов" можно было бы легко "опустить" как минимум
по знаниям паттернов проектирования (особенно сoncurrency) и подходам к разработке сложных систем. Никто не может знать всего.
Если будете у них на собеседовании — в момент, когда от вас потребуют что-либо написать, постарайтесь не отвлекаться
на вопросы "экзаменаторов", "заторапливание" и попытки вывести вас из себя. Иначе наделаете таких ляпов, что самому потом будет стыдно.
Здравствуйте, KhConstantine, Вы писали:
KC> я продемонстрировал также поведение виртуальных функций, вызываемых из конструкторов и деструкторов — вопрос, KC>который почему-то нередко возникает на собеседованиях.
Больная тема, наверное....
К примеру — в последнее время мой IE8 стал слишком часто говорить "pure virtual что-то там call"
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>К примеру — в последнее время мой IE8 стал слишком часто говорить "pure virtual что-то там call"
Очень интересно... Потому что именно MSVC вроде бы матерится на это на этапе ЛИНКОВКИ, а не рантайма...
Мне тоже на одном из собеседований утверждали, что маты идут на этапе рантайма, причем с именно MS.
Интересно было бы пример посмотреть — в реальной жизни я с таким не встречался.
А вот в архиве, на который я сделал ссылку,этот кусочек — как раз на линковке валится:
// базовый для всех объектов, работающих с G
// в том числе и для тех, которым данные из буфера нужныclass AGFuncCaller
{
........
AGFuncCaller(SomeSubSystem &oSomeSubSystem)
{
//dump(); // uncomment it to see the LINKER (not real-time) problem of calling
//abstract virtual function
gBufferSize = 0;
_oSomeSubSystem = &oSomeSubSystem;
getSubSystem()->log("1. Constructor AGFuncCaller said:");
someVirtualFunc();
getSubSystem()->log("1. Constructor AGFuncCaller finishing");
};
......
virtual void dump()=0;
......
};
Здравствуйте, KhConstantine, Вы писали:
КД>>К примеру — в последнее время мой IE8 стал слишком часто говорить "pure virtual что-то там call" KC>Очень интересно... Потому что именно MSVC вроде бы матерится на это на этапе ЛИНКОВКИ, а не рантайма... KC>Мне тоже на одном из собеседований утверждали, что маты идут на этапе рантайма, причем с именно MS. KC>Интересно было бы пример посмотреть — в реальной жизни я с таким не встречался. KC> А вот в архиве, на который я сделал ссылку,этот кусочек — как раз на линковке валится:
А так? (MS VC под рукой нет, поэтому GCC; через промежуточную функцию ):
class Base {
public:
Base () {
anotherFunction ();
}
void anotherFunction () {
pure ();
}
virtual void pure () = 0;
};
class Derived : public Base {
public:
virtual void pure () {
return;
}
};
int main () {
Derived derived;
}
Результат:
pure virtual method called
terminate called without an active exception
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
КД>>К примеру — в последнее время мой IE8 стал слишком часто говорить "pure virtual что-то там call" KC>Очень интересно... Потому что именно MSVC вроде бы матерится на это на этапе ЛИНКОВКИ, а не рантайма...
везёт же некоторым , а вот кто работал с Visual Studio 6.0 знает, что компилятор гордо промолчит и даже никакого warning не будет на 4м уровне.
Во всех последующих версиях студии, генерится ошибка на этапе компиляции.
Здравствуйте, Вертер, Вы писали:
КД>>>К примеру — в последнее время мой IE8 стал слишком часто говорить "pure virtual что-то там call"
В>везёт же некоторым , а вот кто работал с Visual Studio 6.0 знает, что компилятор гордо промолчит и даже никакого warning не будет на 4м уровне.
Навеяло:
— "Предлагаю сразу перейти к третьей степени устрашения. У нас даже генералы плачут как дети."
— "Разрешите присутствовать?!"
Шматрица.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, KhConstantine, Вы писали:
КД>>К примеру — в последнее время мой IE8 стал слишком часто говорить "pure virtual что-то там call" KC>Очень интересно... Потому что именно MSVC вроде бы матерится на это на этапе ЛИНКОВКИ, а не рантайма...
Здравствуйте, Alexey F, Вы писали:
AF>А так? (MS VC под рукой нет, поэтому GCC; через промежуточную функцию ): AF>
AF>class Base {
AF>public:
AF> Base () {
AF> anotherFunction ();
AF> }
AF> void anotherFunction () {
AF> pure ();
AF> }
AF> virtual void pure () = 0;
AF>};
AF>class Derived : public Base {
AF>public:
AF> virtual void pure () {
AF> return;
AF> }
AF>};
AF>int main () {
AF> Derived derived;
AF>}
AF>
Давно известно, что вызов виртуальной функции в конструкторе в C++ — А-ТА-ТА, в отличие, кстати, от ныне полупокойной Delphi. Еще одна веревка для отстрела собственных конечностей, которых в C++, увы (ура?) хватает.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, igna, Вы писали:
I>Здравствуйте, Kernan, Вы писали:
K>>Соглашусь. В catch delete лучше убрать.
I>catch, который ловит что попало и это "что попало" затем не перебрасывает, тоже лучше убрать.
здесь у catch есть одна задача: остановить бабах и дать возможность процедуре восстановиться после него.
Так что, пусть постоит....
Здравствуйте, amberovsky, Вы писали:
A>Я ответил так: A>1. new может не сработать, нужно добавить, например, try/catch.
Думаю, чисто теоретически это все правильно. Но на практике я бы не сильно заморачивался спецзащитой. Ибо нехватка памяти — это конец. Поймаешь здесь — стрельнет в другом месте (где-нить в stdlib).
За 10 лет программерства ни разу не столкнулся с нехваткой памяти под программу на i386. Один раз сделал утечку, система (Linux) умерла в муках, но не призналась.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, amberovsky, Вы писали:
A>>1. new может не сработать, нужно добавить, например, try/catch. К>Лучше не try-catch (это слишком громоздко), а RAII — любой умный указатель или контейнер.
Который будет врапером над try-catch?
A>> Кроме того, g может "испортить" указатели (как тут просто решить я не знаю) К>Паранойя. Чтобы испортить указатели, функция должна расстрелять стек. А это никакими средствами не лечится. К>А вот метнуть исключение функция g() может запросто.
А еще сигнатура функции может принимать ссылку на указатель и творить с ним все что захочет
Здравствуйте, IROV.., Вы писали:
A>>>1. new может не сработать, нужно добавить, например, try/catch. К>>Лучше не try-catch (это слишком громоздко), а RAII — любой умный указатель или контейнер. IRO>Который будет врапером над try-catch?
Нет, RAII — это враппер над try-finally.
IRO>А еще сигнатура функции может принимать ссылку на указатель и творить с ним все что захочет
Ай, шайтан!
Только у нас сигнатура уже прописана, и там указатели передаются по значению.
Здравствуйте, Кодт, Вы писали:
К>Нет, RAII — это враппер над try-finally.
Это чемто концептуально отличаеться от try-catch?
всегда пологал что try-finally это синтаксический сахар
К>Ай, шайтан! К>Только у нас сигнатура уже прописана, и там указатели передаются по значению.
От , не заметил обьявление.
Здравствуйте, IROV.., Вы писали:
К>>Нет, RAII — это враппер над try-finally. IRO>Это чемто концептуально отличаеться от try-catch? IRO>всегда пологал что try-finally это синтаксический сахар IRO>
Они все — синтаксический сахар над longjmp.
Разница в том, что try-except пасёт исключения, а try-finally — пасёт любой исход.
Поэтому, если убирать мусор с помощью try-except, это будет дублирование кода или макароны
Здравствуйте, Кодт, Вы писали:
К>Разница в том, что try-except пасёт исключения, а try-finally — пасёт любой исход.
Ну я как бы в курсе дела, Док.
К>Поэтому, если убирать мусор с помощью try-except, это будет дублирование кода или макароны
Поэтому я и люблю такого рода синтаксический сахар но С++ извольте не дает такой возможности.
конечно можно воспользоваться запрещеным приемом и сворганить чтото типа вот этого try-finally
ИХМО без обоснования, изобретать подсистемы, и интерфесы по типу AGFuncCaller нет никакого смысла, в том числе и промышленных программах, и кстати понятие "промышленный дизайн" вообще осталось для меня не ясным. Для вас "промышленный дизайн", это наличие интерфейса SomeSubSystem и интерфейсов по типу AGFuncCaller?
А код у вас аккуратный, и в хорошем стиле, читается легко и приятно , комментарии ещё б были к функциям и классам в doxygen-е и было б идеально.
Здравствуйте, TarasKo, Вы писали:
TK>ИХМО без обоснования, изобретать подсистемы, и интерфесы по типу AGFuncCaller нет никакого смысла, в том числе и промышленных программах, и кстати понятие "промышленный дизайн" вообще осталось для меня не ясным. Для вас "промышленный дизайн", это наличие интерфейса SomeSubSystem и интерфейсов по типу AGFuncCaller?
Вы правы, конечно. ВЕСЬ этот код — одно сплошное баловство. Написанное просто так, без веской причины. Дизайн создается под конкретную цель.
Здесь — вообще никакой цели. Говоря о "промышленном дизайне" всего-навсего имел в виду, что есть и подходы, целесообразные для небольших задач (которые можно написать в одиночку за разумное время), либо "одноразовых". А то, что для мелких задач является избыточно сложным, становится экономически выгодным на крупных задачах, которые живут долго — и постоянно модифицируются, да притом и разными людьми.
TK>А код у вас аккуратный, и в хорошем стиле, читается легко и приятно , комментарии ещё б были к функциям и классам в doxygen-е и было б идеально.
На это меня уже не хватило .
Здравствуйте, amberovsky, Вы писали:
A>При попытке устроится в одну фирму прислали "удалённое" тестовое задание.
Раз уж это задание попало на RSDN и стало предметом обсуждения, позвольте несколько комментариев от автора.
1. Целью этого задания является предварительный отсев кандидатов. Если кандидат нормально ответил на вопросы, мы приглашаем его на собеседование. Как показывает практика, на этом этапе отсеивается примерно половина кандидатов, что серьёзно экономит наше время.
2. Основным требованием к заданию является простота его выполнения (для тех кандидатов, которые нам подходят). Сложные задания отпугивают кандидатов, а нам это не нужно.
3. Никаких "единственно правильных ответов" не существует. Ответы рассматривают разработчики, а не отдел кадров. На этом этапе мы готовы принять любой вариант, который решает больше проблем, чем создаёт.
4. Мы тоже иногда ошибаемся, причём при наличии сомнений предпочитаем отказать кандидату. В конце концов, каждый ошибочно принятый на работу человек обходится нам в несколько десятков тысяч рублей чистого убытка, а каждый ошибочно отвергнутый — найдёт себе применение где-нибудь в другом месте.
5. Если кандидат в процессе чтения этой ветки узнаёт много нового и интересного для себя — это верный признак того, что ему ещё есть чему поучиться до того, как он сможет претендовать на работу у нас.
Здравствуйте, March_rabbit, Вы писали:
M_>Здравствуйте, amberovsky, Вы писали:
A>>Я ответил так: A>>1. new может не сработать, нужно добавить, например, try/catch. M_>Думаю, чисто теоретически это все правильно. Но на практике я бы не сильно заморачивался спецзащитой. Ибо нехватка памяти — это конец. Поймаешь здесь — стрельнет в другом месте (где-нить в stdlib).
M_>За 10 лет программерства ни разу не столкнулся с нехваткой памяти под программу на i386. Один раз сделал утечку, система (Linux) умерла в муках, но не призналась.
Это не так. Есть ситуации когда ловля bad_alloc спасает ситуацию.
Например при попытке выделить огромный объем памяти ее может не хватить — не рушить же из-за этого программу. Можно поймать bad_alloc и например попробовать выделить поменьше.
Если программа работает в критичном окружении (сервер, обрабатывающий тысячи запросов), на очередную операцию может запросто не хватить ресурсов. Рушить сервер в данном случе в корне неверно — нужно наоборот предпринять усилия спасти его, например перестать принимать запросы пока не освободиться память.
На практике тоже не сталкивался, но генерил такую ситуацию (Vista 32 bit, 3 Gb RAM) — ислючение отработало, винда выдержала.
Здравствуйте, alxn1, Вы писали:
A>Офигеть. Сколько негатива и неуважения. Даже "Вы" выглядит издевкой.
"Вы" — это не издёвка, а вежливость. Очень полезная штука: она позволяет более-менее сносно общаться людям в конфликтной ситуации. А негатив и неуважение — всё-таки лучше неискренности в стиле "Вы замечательно ответили на все наши вопросы и очень нам понравились, но работу поищите в другом месте".
A>Ждем и правда вашего кода.
Зачем? В данном случае оценка моей квалификации зависит не от моего умения писать код, а от того, правильно ли я оценил кандидата. Если Вы считаете, что я ошибся — пожалуйста, берите его себе.
Надеюсь, Вы чувствуете границу между защитой чести и достоинства и самоутверждением. Я эту границу переходить не хочу.
Здравствуйте, KhConstantine, Вы писали:
KC> Покажите нам, КАК ВЫ САМИ пишете серьезные приложения — отнюдь не тексты под копирайтом, KC>о нет! И никто вас не просит выложить нечто, что требует больше нескольких часов работы.
Вы предлагаете мне быстренько, на коленке, за пару часов написать "серьёзное приложение"?
Или Вы предлагаете написать что-нибудь простенькое, но использовать при этом избыточно "тяжёлый" подход?
KC>Дайте же нам идеал, к которому нам надлежит стремиться!
Отстаньте, нет у меня этого идеала. И нимба вокруг головы — тоже нет.
KC> Вы же сами критики не боитесь?
Вам угодно считать меня плохим программистом? Ради бога. Главное — не ставьте мои копирайты на свой код и воздержитесь от описаний моего мнения.
Вам угодно заняться фаллометрией на форуме? С этим — не ко мне.
Luck in life always exists in the form of an abstract class that cannot be instantiated directly and needs to be inherited by hard work and dedication.
Здравствуйте, TarasKo, Вы писали:
TK>Советую забить У меня тоже бывали случаи, когда после собеседования хотелось кричать и махать кулаками, мне казалось что со мной поступили не справедливо, я тоже писал на рсдн например здесь TK>http://www.rsdn.ru/forum/philosophy/2971148.aspx
TK>Теперь я по другому к этому отношусь. Конечно собеседующие могут быть не идеальны, но эта та часть мира которую мне не изменить. Зато я могу изменить себя. Могу читать больше книжек, могу учиться разговаривать по другому, так что бы не возникало желания меня гнобить Вспоминая то собеседование, могу сказать что я был очень самоуверен, вел себя соответствующе, быстро и слишком подробно ответил на вводные вопросы про с++, мне иногда казалось, что собеседующий меня не понимает. Видимо этим его разозлил
TK>Собеседование это ж не только проверка на знание, но и проверка насколько я коммуникабелен. Если так получилось что за время собеседование я разозлил интервьюера, то это тоже повод задуматься. Меня не возьмут на работу даже если я буду ВСЁ знать, ведь как потом со мной работать.
TK>Вообщем я из того случая надеюсь извлёк свои уроки Вам сооветую сделать тоже самое и не на кого не злится
У меня с коммуникабельностью все в порядке, спасибо. Выводы тоже давно сделал. В частности, пришел к выводу, что если на собеседовании возможные коллеги из числа "равных" не могут вытерпеть отношение "равный к равному" — потом тем более не смогут. Думаю, что ставить себя в позу "послушный мальчик" на собеседовании имеет смысл только в особых случаях.
А вот предела совершенству в профессиональной сфере — действительно нет.
Здравствуйте, sergey_cheban, Вы писали:
_>Здравствуйте, alxn1, Вы писали:
A>>Офигеть. Сколько негатива и неуважения. Даже "Вы" выглядит издевкой. _>"Вы" — это не издёвка, а вежливость. Очень полезная штука: она позволяет более-менее сносно общаться людям в конфликтной ситуации. А негатив и неуважение — всё-таки лучше неискренности в стиле "Вы замечательно ответили на все наши вопросы и очень нам понравились, но работу поищите в другом месте".
A>>Ждем и правда вашего кода. _>Зачем? В данном случае оценка моей квалификации зависит не от моего умения писать код, а от того, правильно ли я оценил кандидата. Если Вы считаете, что я ошибся — пожалуйста, берите его себе.
_>Надеюсь, Вы чувствуете границу между защитой чести и достоинства и самоутверждением. Я эту границу переходить не хочу.
По-моему, Вам не помешает познакомиться с работами Эрика Берна и книжкой русского автора "Игры, в которые играют менеджеры" (автора не помню). Если еще не знакомы, конечно. Ну а негатив и неуважение никогда полезными не бывают.
Оптимальный стиль общения — нейтрально-положительный. Безэмоциональная формулировка решения, опционально — причин решения.
Здравствуйте, sergey_cheban, Вы писали:
_>Здравствуйте, KhConstantine, Вы писали:
KC>> Покажите нам, КАК ВЫ САМИ пишете серьезные приложения — отнюдь не тексты под копирайтом, KC>>о нет! И никто вас не просит выложить нечто, что требует больше нескольких часов работы. _>Вы предлагаете мне быстренько, на коленке, за пару часов написать "серьёзное приложение"? _>Или Вы предлагаете написать что-нибудь простенькое, но использовать при этом избыточно "тяжёлый" подход?
Именно простенькое, "на коленке", но с "тяжелым" подходом. Если под словом "тяжелый" вы понимаете те подходы, что следует применять
при коллективной разработке сложных систем по вашему мнению.
И не бойтесь, что я отнесусь к нему так же, как вы отнеслись к моему написанному на коленке за пару часов коду.
У меня вообще нет привычки "опускать" собеседников, тем более — распуская пальцы веером над приложением, которое нет смысла вылизывать и документировать. А то, что в любом коде, написанном "на автомате" и не отсмотренном повторно коде ляпы будут, и много — очевидно.
Более того, и в повторно отсмотренном самим программистом коде встречаются ляпы, ибо глаз замыливается. Потому во многих конторах еще и
коллективный анализ каждого изменения выполняется. Так что по вашим ляпам от оскорбительных комментариев я воздержусь.
Мне просто интересно — а вдруг я действительно увижу нечто новое для себя?
KC>>Дайте же нам идеал, к которому нам надлежит стремиться! _>Отстаньте, нет у меня этого идеала. И нимба вокруг головы — тоже нет.
KC>> Вы же сами критики не боитесь? _>Вам угодно считать меня плохим программистом? Ради бога. Главное — не ставьте мои копирайты на свой код и воздержитесь от описаний моего мнения. _>Вам угодно заняться фаллометрией на форуме? С этим — не ко мне.
Пока что есть основания считать вас лишь критиком, причем худшего сорта.
И мне совершенно все равно, лучше вы меня как программиста или хуже. Вы не тот эталон, с которым имеет себя сравнивать.
_>4. Мы тоже иногда ошибаемся, причём при наличии сомнений предпочитаем отказать кандидату. В конце концов, каждый ошибочно принятый на работу человек обходится нам в несколько десятков тысяч рублей чистого убытка, а каждый ошибочно отвергнутый — найдёт себе применение где-нибудь в другом месте.
Странно, что вы не понимаете, что ошибочно отвергнутый на самом деле обходится в сотни тысяч рублей убытка.
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>2.
A>int main(int argc, char* argv[])
A>{
A> if( argc > 1 )
A> printf( argv[1] );
A> return 0;
A>}
A>
Основная проблема — необходимость переписать этот код полностью. А так он нормальный.
UP!!!
Во, блин. Мне тож drWeb говнокод прислал, даже и не знаю чего им ответить.
Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
1.
void g( char *, char *, size_t );
void f( size_t n )
{
char *a = new char[n];
char *b = new char[n];
g( a, b, n );
delete [] b;
delete [] a;
}
Здравствуйте, Tesh, Вы писали:
T>Здравствуйте, любой, Вы писали:
Л>>Здравствуйте, amberovsky, Вы писали:
Л>>Тут по существу уже сказали. А меня в таком коде всегда прикалывает, почему два блока нельзя за раз выделить:
Л>>char *a = new char[n*2]; Л>>char *b = a + n; Л>>g( a, b, n ); Л>>delete [] a;
T>Насколько я понимаю в этом случае нужен будет в два раза больший непрерывный блок памяти, который найти меньше шансов, чем два поменьше
В общем случае это не предсказуемо из за фрагментации этих блоков поменьше на еще более поменьше — один выделен, второй нет. А чем больше фрагментация — том дольше искать следующий блок.
Зато операция выделения/освобождения — одна. Что без шансов в ~два раза быстрей & меньше фрагментация памяти. Единственный минус — надо быть внимательней самому и тем кто этот код будет править.
На первый вопрос ответил, что текст не отформатирован, переменные и названия функций не несут смысловой нагрузки. Я не должен делать предположения по поводу тела функции g и поведению операторов new, без большей информации нет смысла искать проблемы. На второй ответил, что следуют заменить printf(argv[1]) на puts(argv[1]). Может они хотели услышать про уязвимость printf или еще чего. На досуге подумаю. Смысл в этом задании ведь должен какой-то быть.
Здравствуйте, lennyn, Вы писали:
L>UP!!! L>Во, блин. Мне тож drWeb говнокод прислал, даже и не знаю чего им ответить.
L>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать? L>1. L>void g( char *, char *, size_t ); L>void f( size_t n ) L>{ L>char *a = new char[n]; L>char *b = new char[n]; L>g( a, b, n ); L>delete [] b; L>delete [] a; L>}
L>2. L>int main(int argc, char* argv[]) L>{ L>if( argc > 1 ) L>printf( argv[1] ); L>return 0; L>}
Ну раз это доктор Вэб, то
1. Я бы ответил что в функции f n не проверяеться размер n ,можно запросить столько памяти что не сможет выделить система
2. Стопудово что размер argv[1] не проверяеться, программа становиться уязвимой
A>Ну раз это доктор Вэб, то A>1. Я бы ответил что в функции f n не проверяеться размер n ,можно запросить столько памяти что не сможет выделить система
А если задача работать быстро, размер n заведомо не превосходит доступной памяти, а лишние проверки только затормозят?
A>2. Стопудово что размер argv[1] не проверяеться, программа становиться уязвимой
Тогда еще надо проверять и все возможные модификаторы в строке ввода, вообще есть готовые функции для разбора опций, чтобы велосипед не изобретать. А тут может быть программа — это затычка для какого-нибудь скрипта и говорить про уязвимость бессмысленно.
В общем, как я уже написал, на практике без дополнительных не видно смысла что-то с этим делать, тест, строго говоря, некорректный.
M>В общем, как я уже написал, на практике без дополнительных не видно смысла что-то с этим делать, тест, строго говоря, некорректный.
А представьте, каково нам. Мы решаем задачу, для которой математически строго доказана невозможность корректного решения: определяем, является ли файл вирусом. Ничего, как-то справляемся.
Здравствуйте, alexander_st, Вы писали:
S>>Нет, для того, чтобы показать что этот буфер изменять не надо. _>из каких соображений? что будет если его изменить?.... точнее чем изменение буфера хуже его удаления?
Тем что его уже может не существовать в момент изменения, что может повлеч расстрел памяти с продолжением работы. А вот освобождение не выделенной памяти с высокой вероятностью бужет пресечено сразу же.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
SaZ>Признаю, что возможно немного погорячился с оценкой. Цепляться в этом случае или нет, так сказать, человеко-зависимо. Мои преподаватели бы зацепились (на собеседовании, а на экзамене — тем более).
Это очень от целей собеседования/экзамена зависит...
Но, в любом случае, в ответе всякую ересь можно был бы и не писать...
Кстати, проблема может быть и в том, что вообще используется функция fprintf, а не std::cout, например
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
V>Странно, что вы не понимаете, что ошибочно отвергнутый на самом деле обходится в сотни тысяч рублей убытка.
*недополученной прибыли
Несколько разные вещи.
M>C учетом предлагаемой зарплаты, других кандидатов вы наврятле увидите усебя на собеседовании
Хм. Как-то я пропустил, что это ДрВеб.
А вакансия забавная. Я слабо представляю, кто с указанным набором знаний пошел бы на такую зп вообще. Видимо, надеются, что кто-то пойдет работать "за громкое имя", ну так на это только студенты ведутся.
Новости очень смешные. Зря вы не смотрите. Как будто за наркоманами подсматриваешь. Только тетка с погодой в завязке.
There is no such thing as a winnable war.
Бредовые тестовые вопросы составлены человеком, который не умеет собеседовать. В вопросе отсутствует какой либо смысл... что хотел узнать человек, не понятно...
Здравствуйте, Кодт, Вы писали:
К>Вот если бы функция была объявлена void g(char*,char*,size_t)throw() — это другое дело... Но она же не объявлена.
На VS throw() ни к чему не обязывает и ничего не гарантирует. Компилятор будет бросать варнинги о том, что это не реализовано, лечиться отключением через #pragma.
Здравствуйте, Vamp, Вы писали:
V>Где? В вызывающей функции он есть. Но я вообще-то не считаю, что bad_alloc надо перехватывать, за исключением редких стратегий типа — попросим 10 гиг, не получилось — тогда 5, опять не получилось, тогда 2 Кб. Как правило, программа просит столько памяти, сколько ей надо для работы
В Linux (и в винде вроде тоже) есть спекулятивное выделение памяти т.е. можно запросить и 10 гиг и получить добро от ОС, креш произойдёт потом когда приложение попробует заюзать полностью эти недоступные 10 гиг, oom killer прибъёт.
E__>Здравствуйте, Eugeny__, Вы писали:
E__>Здравствуйте, midl, Вы писали:
M>>C учетом предлагаемой зарплаты, других кандидатов вы наврятле увидите усебя на собеседовании
E__>Хм. Как-то я пропустил, что это ДрВеб.
E__>А вакансия забавная. Я слабо представляю, кто с указанным набором знаний пошел бы на такую зп вообще. Видимо, надеются, что кто-то пойдет работать "за громкое имя", ну так на это только студенты ведутся.
Ну вообще-то, да! Странная контора?? На моей памяти они уже лет 8 наверно ищут С++ программиста и регулярно несколько раз в месяц постят вакансию на разных сайтах??? Толи они ищут как ищут жену- одну и на всю жизнь, толи они набирают С++ программистов, пока не заполнят вагон, а как заполнят на этап их в Освенцим и потом берут новый вагон.
Здравствуйте, artkarma, Вы писали:
E__>>Хм. Как-то я пропустил, что это ДрВеб.
E__>>А вакансия забавная. Я слабо представляю, кто с указанным набором знаний пошел бы на такую зп вообще. Видимо, надеются, что кто-то пойдет работать "за громкое имя", ну так на это только студенты ведутся. A>Ну вообще-то, да! Странная контора?? На моей памяти они уже лет 8 наверно ищут С++ программиста и регулярно несколько раз в месяц постят вакансию на разных сайтах??? Толи они ищут как ищут жену- одну и на всю жизнь, толи они набирают С++ программистов, пока не заполнят вагон, а как заполнят на этап их в Освенцим и потом берут новый вагон.
Ну, с такими требованиями и зарплатой они будут искать лохов еще долго, трудно, и под смех адекватных людей.
Новости очень смешные. Зря вы не смотрите. Как будто за наркоманами подсматриваешь. Только тетка с погодой в завязке.
There is no such thing as a winnable war.
Здравствуйте, любой, Вы писали:
Л>А меня в таком коде всегда прикалывает, почему два блока нельзя за раз выделить:
Л>char *a = new char[n*2]; Л>char *b = a + n; Л>g( a, b, n ); Л>delete [] a;
Не забывай про то, что этот код будет потом читаться\переделываться другими. Я бы "споткнулся" об это место при чтении, а читаю чужого кода я довольно много.
Поверьте, из 10 кандидатов, только 2-3 человека могут указать на все проблемы.
Порядка половины может указать на одну или две.
А вот предложить решение, это уже какой-то высший пилотаж...
П.С.
И этот код я скоммуниздил из реального(!) проекта. Естественно я его причесал и выкинул ненужное, но суть та же.
П.П.С.
А вот ЧСВ у многих товарищей соискателей просто зашкаливает. Вы на улицу давно выглядывали, с людьми
общались? или только в мониторы смотрите, да в теплых помещениях сидите?
1. Не инициализируется массив. Не критично, но не помешает.
2. Не контролируется размер передаваемых данных при копировании в массив.
3. Возвращается указатель на массив, который был создан в теле функции.
По хорошему нужно передавать в функцию два массива — источник и приемник. Проверять размерности. Использовать strncpy. м?
N>1. Не инициализируется массив. Не критично, но не помешает.
*зачем* его инициализировать?
чтоб подавить предупреждение о неинициализированной переменной?
или чтоб в 99.9% случаев баг с отсутствием нуля на конце строки спрятать?
N>>1. Не инициализируется массив. Не критично, но не помешает. A>*зачем* его инициализировать? A>чтоб подавить предупреждение о неинициализированной переменной? A>или чтоб в 99.9% случаев баг с отсутствием нуля на конце строки спрятать?
Здравствуйте, nazavrik, Вы писали:
N>Здравствуйте, Stepkh, Вы писали:
S>>С вопросом: что не нравится, какие есть проблемы и как бы вы решили:
N>Интереса для.
N>1. Не инициализируется массив. Не критично, но не помешает. N>2. Не контролируется размер передаваемых данных при копировании в массив. N>3. Возвращается указатель на массив, который был создан в теле функции.
N>По хорошему нужно передавать в функцию два массива — источник и приемник. Проверять размерности. Использовать strncpy. м?
Здравствуйте, Stepkh, Вы писали:
S>Здравствуйте, amberovsky, Вы писали:
A>>Здравствуйте. A>>При попытке устроится в одну фирму прислали "удалённое" тестовое задание.
S>Да абсолютно нормальное задание. Конечно лучше подобное давать тет-а-тет, а не удаленно. Но...
S>Вот есть мой тестовый кусок кода, который даю кандидатам. S>С вопросом: что не нравится, какие есть проблемы и как бы вы решили:
S>Поверьте, из 10 кандидатов, только 2-3 человека могут указать на все проблемы. S>Порядка половины может указать на одну или две.
есть подозрение, что половина этих "проблем" — надуманные проблемы.
у этого кода только одна проблема — он не компилируется. (при включенных предупреждениях компилятора которые трактуются как ошибки)
при попытке ее решить возникает другая проблема — это код на Си.
дальше можно только добавить, что в моем компиляторе strcpy перегружен шаблоном функции и с размером "tmp" никакой проблемы нет, как Вы возможно считаете.
также "с" мог бы иметь тип "const char*, но это не проблема этого кода, может так было сделано специально,
а 80 такое же магическое число, как и осмысленные имена "foo" и "c"
A>есть подозрение, что половина этих "проблем" — надуманные проблемы.
A>у этого кода только одна проблема — он не компилируется. (при включенных предупреждениях компилятора которые трактуются как ошибки) A>при попытке ее решить возникает другая проблема — это код на Си.
A>дальше можно только добавить, что в моем компиляторе strcpy перегружен шаблоном функции и с размером "tmp" никакой проблемы нет, как Вы возможно считаете. A>также "с" мог бы иметь тип "const char*, но это не проблема этого кода, может так было сделано специально, A>а 80 такое же магическое число, как и осмысленные имена "foo" и "c"
Вы наверно невнимательно читали исходный пост — этот код был взят из реального(!) проекта.
И жил там полтора года, пока не пришлось брать в руки большой напильник и чинить, чинить.
По поводу не компилируется: всё зависит от вашего компилятора. Никто не помешает вам, как истинному джедаю,
отключить все предупреждения для конкретного файла. (И ведь делают так!!!) А потом приходится лезть в makefile
и материться и материться.
Вот, например, gcc 2.7 даже не муркнет на этот код. Да, древний компилятор, но.. не мы устанавливаем правила.
А вот gcc постарше кинет предупреждение:
main.cpp: In function ‘char* foo(char*)’:
main.cpp:6:9: warning: address of local variable ‘tmp’ returned [enabled by default]
И что? да ничего — соберется все за милую душу.
А что будет мешать нам собираться — то удалим (-Werror нам мешает)
Тут уже больше относится к дисциплине, но, ведь человек должен понимать что он делает?
я пару раз давал такое задание , и встречал полное непонимание.
-----
" Програмисту дали задание реализовать функцию которая возвращает текущее время с точностью до секунды. В результате он показал заголовочный файл с таким объявлением
currentTime.h
int getCurrentTime();
Вопрос , какие вы видите проблемы и как бы вы смогли улучшить этот код?
"
-----
Интересно что скажет уважаемый All на это задание ?
Здравствуйте, Abyx, Вы писали:
A>*зачем* его инициализировать? A>чтоб подавить предупреждение о неинициализированной переменной? A>или чтоб в 99.9% случаев баг с отсутствием нуля на конце строки спрятать?
Ну это как в анекдоте. У Маши было 8 яблок. 2 яблока она отдала Пете. Вопрос: сколько яблок у Пети?
S>Поверьте, из 10 кандидатов, только 2-3 человека могут указать на все проблемы. S>Порядка половины может указать на одну или две.
Проблем в этом коде всего две.
1. Это не C++
2. Смысла в этом коде нет.
Ввиду пунка 2 искать дальнейшие проблемы и спорить до усера о необходимости инициализации tmp и допустимости возврата указателя на локальную переменную совершенно бессмысленно, этот код подлежит отправке "на помоечку".
Tы знаешь, твои задачки трудны не тем, что они какие-то шибко закавыристые, а тем, что непонятно зачем вообше все это надо. Все таки должна быть хоть какая то логика пусть даже и в тестовых задачах. Подозреваю, что многие из опрошенных были в ступоре от функции, которая возвращает ту же самую информацию, что была в нее подана как аргумент, да еще предварительно покопировав ее в локальную строку. Собеседование — это тот еще стресс. Ты бы хотя бы функции и пременные назвал так, чтобы человек с без телепатических способностей мог догадаться, что твоя функция фууу должна была делать. Короче, правильный ответ на вопрос что не нравится — "Да, все", а как исправить — "А что надо-то?".
По поводу этой фуу с маллоком, тоже неясно, зачем она вообще и как ты хочешь ее улучшить. Что в ней плохого? То, что она без единого free? Так может она и есть аллокатор, и удалять не ее забота. Или ты намекаешь что функция большое_веселье должна была удалять, так опять не понятно зачем ей столько буферов в цикле если можно было бы одним обойтись, причем не обязательно динамическим. Да и статик/нестатик — как это влияет: разве функция не возвращает адрес из маллока? Ты что убрал статик — и сразу все починилось?