Подтвердить что ты не робот

Самые трудные для поиска ошибки в С++

Мне хорошо известно, что, вероятно, не будет "самой трудной для поиска ошибки в С++", но меня все еще интересует то, что другие люди могут подумать/могли уже встретить.

Идея этого вопроса возникла во время обсуждения с другом. Мы согласились, что должно быть довольно просто саботировать проект cpp, в частности, в том числе ошибки в исходном коде, который вы отправляете... Но самое лучшее, что мы могли подумать, это использовать неинициализированные переменные (приводящие к случайным ошибкам сегментации во время выполнения), Я уверен, что есть лучшие способы...?!

Требуемые характеристики неисправного кода:

  • должен выглядеть как действительный код с первого взгляда
  • не должен останавливать компиляцию кода (слишком очевидно)
  • если возможно, ошибка должна выглядеть так, как будто это может быть ошибкой (если она когда-либо будет найдена)
  • ошибка должна быть достаточно серьезной, чтобы остановить загрузку программного обеспечения (например, случайные сбои seg, логический сбой кода и т.д.).

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

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

Короче:

Какой самый тонкий способ саботировать исходный код, который может остаться незамеченным в дифференциальной фиксации (например, git), но в конечном итоге будет препятствовать выпуску программного обеспечения?

4b9b3361

Ответ 1

Не слишком очевидно:

if (foo =! foobar)

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

 if ( (i =! 3) && (j==1))

Ответ 2

Classic:

#define if while
#define else

похоронен в каком-то заголовке.


Застревание в комментарии @WhozCraig, также:

#define true (!!(__LINE__ % 10))

Как только каждые десять строк true не будут такими true, но скомпилированное поведение программы останется постоянным... изменить необъяснимо, когда что-то изменится в источниках.

Вдоль этой строки:

#define for if(__LINE__ % 10) for

#define NULL (!(__LINE__ % 10))

Или как насчет:

#define virtual 

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


Аналогично:

#define dynamic_cast static_cast

// Fail early, fail often
#define throw std::abort();

Ответ 3

Я был когда-то задержан в течение большей части месяца, потому что в выпуске сборки сортировка нашего CArray (от Microsoft как часть MFC) была бы случайной segfault, но отладочные сборки были прекрасны. Мы заменили его на std::vector, и проблема была решена. Только через несколько месяцев кто-то сообщил мне, что CArray не использует оператор присваивания элементов и вместо этого использует memcpy (источник). Это явно искажает любые содержащиеся объекты с нетривиальным оператором присваивания, но это стандартный контейнер, поэтому все считают его безопасным. Поэтому, если заменить std::vector на CArray в нескольких ключевых местах...

В качестве примечания Microsoft заявляет, что вместо этого использовать контейнеры MFC и использовать контейнеры STL теперь.

Ответ 4

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

Если у вас есть жестко закодированные строки байтов, все одинаковые длины (например, что-то, что вы могли бы использовать с сетью), вы можете воспользоваться возможностью, чтобы замаскировать его:

const unsigned char someBytes[] = "text\0abc123";

может с помощью небольшого переключателя стать:

const unsigned char someBytes[] = "text\0123abc";

Разница в том, что первая имеет 12 символов, а вторая имеет только 10 символов, из-за восьмеричного литерала посередине. Если ситуация возникнет, это, несомненно, будет усугубляться, чтобы отследить.

Ответ 5

Я бы сказал, что самая неприятная вещь для меня использовала = вместо ==. Например:

while(foo = bar) {}

вместо

while(foo == bar) {}

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

Почетные упоминания:

  • Использование неправильного математического оператора - + / *
  • Подобно = vs ==, ошибся & для && или | для ||.
  • Преждевременная оптимизация в чем-то вроде vector<bool> (Читать здесь, если вы все похожи на "что?" )
  • Наличие двух или более классов с тем же именем и неправильного использования.

Ответ 6

struct
{ int foo
char bar
}


while (foobar != 10);
{
     //do something here
 } 

1) Забыв поставить a; после структуры ИЛИ положить a; после цикла WHILE

randn() //user created function
rand()  //library function

2) Именование функции, которая имеет аналогичное имя для предопределенной функции

Ответ 7

Не совсем ошибка, но я делаю это где-то в случайном исходном файле, когда мне нахожу, что это раздражает, когда сборки релизов медленнее, чем отладочные сборки.:)

#ifdef NDEBUG
namespace {
    struct foo {
        foo() { sleep(rand() % 4); }
    } bar;
}
#endif

Ответ 8

Не были жертвой этого, но неявные преобразования могут привести к некоторым плохим вещам. Посмотрите на это:

class Foo {
public:
    Foo(int a, OtherClass* b = NULL);
};

Теперь (без явного ключевого слова) каждый метод, ожидающий ссылки Foo по значению /const, также примет int!!!