Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо
to the top

Конкурс самых прикольных, глупых и страшных ошибок на C++!

Поделитесь своим кодом с ошибкой и участвуйте в розыгрыше 10 книг "Вредные советы для C++ программистов" с подписью автора.

Что за приз?

Photo of Andrey Karpov and his book

Аннотация: в книге читателю предлагается список "советов", которым не стоит следовать. Каждый совет сопровождается подробным разбором и рассмотрением неочевидных моментов. Эти пояснения будут полезны новичкам, изучающим программирование. Впрочем, книга развлечёт и профессионалов рассмотрением некоторых нюансов программирования на C++.

Автор: Карпов Андрей Николаевич, к.ф.-м.н. Более 15 лет занимается темой статического анализа кода и качества программного обеспечения. Автор большого количества статей, посвящённых написанию качественного кода на языке C++. С 2011 по 2021 год удостаивался награды Microsoft MVP в номинации Developer Technologies. Один из основателей проекта PVS‑Studio.

Как участвовать в конкурсе?

Опубликуйте свой код на этой странице до 30 декабря 2023 года, и в указанную дату наша команда отберёт 10 наиболее интересных ошибок. В январе мы свяжемся с победителями для отправки приза — книги "Вредные советы для C++ программистов" с подписью автора. При отправке примеров кода через форму ниже вы соглашаетесь на то, что мы используем эти материалы для отдельной публикации в нашем блоге.

Как добавить свой пример кода с интересной ошибкой?

Введите код на языке C или C++ в поле “Код”, укажите ваше имя (оно будет отображаться рядом с вашим кодом) и email (не будет публиковаться, нужен для связи с победителями). По желанию сопроводите свой код какой-то историей или описанием ошибки. Можете опубликовать несколько вариантов кода с разными и интересными ошибками, тогда шансов на выигрыш будет больше.

Внимание: код будет опубликован на сайте. Поэтому воздержитесь от публикации кода, если это нарушает какие-то условия, например NDA с вашим работодателем. Или перепишите код, чтобы он отражал суть, не раскрывая закрытую информацию, — являлся синтетическим примером.

* Нажимая на кнопку, вы даете согласие на обработку своих персональных данных. См. Политику конфиденциальности.
А также на то, что указанный вами код будет опубликован здесь и в дальнейшем может быть использован для написания статьи.


Код участников

Петр 11.12.2023
#include <algorithm>
#include <vector>
#include <iostream>

std::vector<int> vec{1, 2, 3, 4, 5};
std::transform(vec.begin(), vec.end(), vec.begin(),	[](auto& c){return c*2;});
std::cout << vec << '\n';

К концу очередного осеннего курса питона, после очередного объяснения, чем отличается список от массива numpy, решил для удовольствия написать умножение вектора на число на С++. Написал и уставился в список ошибок компилятора. Тупил долго, смеялся еще дольше. Стиль питона явно угадывался.


Ян 08.12.2023
char* szOriginal = (char*)malloc(100);
strcpy(szOriginal, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
char* szShift = (char*)malloc(100);
strcpy(szShift, "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ");
szShift += iShift;

//...
		
free(szOriginal);
free(szShift); // тут проблемка

Всего лишь в строке 10 память освобождается неверно. Перед 10-ой строкой надо было сделать 'szShift -= iShift;', т. е. обратить операию в пятой строке. Но самое интересное, что это вообще ни на что не повлияло, ведь код был написан в рамках тренировки к олимпиаде по информатике для школьников, и программа проходила все тесты, т. е. завершалась корректно и выдавала правильный ответ.


Николай 07.12.2023
Debug() << "Test" << testName << (result ? "FAILED" : "successed");

Мы писали свою библиотеку для юнит-тестов, я добавил туда такую прекрасную строчку кода :) В итоге потом разбирались как мы так сервер сломали, что ни один тест не проходит, а на деле просто FAILED и successed местами перепутаны были, глупее и придумать нельзя)


Sergey Kushnirenko 05.12.2023
algorithm.h
...

template <class _T, size_t _N>
inline constexpr _T*            begin(_T (&_array)[_N]) { return _array; }

template <class _T, size_t _N>
inline constexpr _T*            end(_T (&_array)[_N]) { return _array + _N; }

...

gtl\algorithm.h(9,30): error C2065: 'L': undeclared identifier
gtl\algorithm.h(9,30): error C2065: '_array': undeclared identifier
gtl\algorithm.h(9,24): error C7525: inline variables require at least '/std:c++17'
gtl\algorithm.h(9,48): error C2447: '{': missing function header (old-style formal list?)
gtl\algorithm.h(12,28): error C2065: 'L': undeclared identifier
gtl\algorithm.h(12,28): error C2065: '_array': undeclared identifier
gtl\algorithm.h(12,46): error C2447: '{': missing function header (old-style formal list?)
gtl\algorithm.h(15,25): error C2365: 'gtl::begin': redefinition; previous definition was 'data variable'
gtl\algorithm.h(24,25): error C2365: 'gtl::end': redefinition; previous definition was 'data variable'

Гдето по пути попалось включение виндовых хедеров а там _T это L"", и препроцессор радостно и с песней заменил эти _T в шаблонах


Николай 02.12.2023
struct IFace { ..... };
class TheImpl : public IFace { ..... };
class TheFixture {
public:
    IFace& iface() { return aggregate_; }
private:
    TheImpl aggregate_;
    // и ещё всякий клей, вспомогательные объекты, связи и функции
};

class TheProvider {
public:
    std::shared_ptr<IFace> provide() {
        if (need_create()) create_cached_object();
        return cached_object_;
    }
private:
    std::shared_ptr<IFace> cached_object_;
    bool need_create() { return !cached_object_ || ....; }  // кеша нет, кеш протух, всё такое...
    // вот тут живёт бажок!
    void create_cached_object() {
        auto cached_object = std::make_shared<TheFixture>();
        cached_object_ = std::shared_ptr<IFace>{cached_object_, &cached_object->iface()};
    }
};

shared_ptr позволяет владеть объектом-сборкой, отдавая интерфейс на агрегат. Например, указатель на узел дерева или даже на содержимое узла дерева. https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr - сигнатура номер 8, "aliasing constructor" Это даёт возможности очень гибко создавать всякие нетривиальные реализации интерфейсов. Проблема в том, что нет никакого контроля - ни типа, ни значения аргументов такого конструктора. Можно делать вот такие странные указатели: https://gcc.godbolt.org/z/ncz88ovW4 Тем не менее, заниматься самоприсваиванием с изменением указателя на агрегат - вполне легально и даже может иметь практический смысл: https://gcc.godbolt.org/z/edEzMe953 Но с такой же лёгкостью мы можем получить преждевременный конец времени жизни! Что я и получил, сделав опечатку в реальном проекте! Здесь привожу иллюстрацию без NDA-подробностей. Дистиллированный код - с багом и с исправлением: https://gcc.godbolt.org/z/x53daYc5P ----- Заодно запостил на RSDN: https://rsdn.org/forum/cpp/8644453.flat#8644453


Константин 28.11.2023
time64_t time_us; // time64_t is int64_t, микросекунды
float LineConvert( float value, float x1, float x2, float y1, float y2);

// Где-то в коде
...
time_us -= LineConvert( phase, 0.0f, 360.0f, 0.0f, period );
...

Не учтено( либо упущено в результате рефакторинга) продвижение типов. В результате через ПАРУ ЧАСОВ терялись значимые микросекунды. код переставал работать, как от него ожидалось. p.s. Масло в огонь подливало то, что заход в данный участок кода зависело от настроек устройства, которое редко выставлялось.


Xintrea 23.11.2023
QString logFileName="/Directory/Logs/Log.txt";

const char *fileName = logFileName.toStdString().c_str();

Ошибка использования указателя на временный объект. Проблема в выражении: const char *fileName = logFileName.toStdString().c_str(); Когда срабатывает метод toStdString(), создается временный объект типа std::string как возвращаемое значение данного метода. Потом для этого временного объекта вызывается c_str(). Полученный указатель запоминается в fileName, но затем временный объект удаляется (ведь работа выражения завершена, объект std::string ничему не присвоен а значит и не нужен), и начинается выполнение следующей команды. Таким образом, указатель fileName начинает указывать на память, которая была выделена объекту, но затем сразу после завершения команды была реаллоцирована под другие данные.


Александр 23.11.2023
try
{
    try
    {
        if( a != 0 ) throw Exception("a != 0");
        else return;
    }
    catch(Exception& e)
    {
        // a != 0
    }
}
__finally
{
    // a != 0
}

Ожидалось, что в блок __finally попадём в любом случае. Оказалось, что при a == 0 выход происходит немедленно


idimus 22.11.2023
// separate header with platform specific defines
#define MIN_PRIORITY 256
#define MAX_PRIORITY 0


// lines of usage in implementation
if (priority > MIN_PRIORITY && priority < MAX_PRIORITY)
{
    SetPriority(priority);
}

На одной из платформ приоритетность реверсивная. Статистический анализ на ней не запускался на CI.


qraizer 21.11.2023
struct B
{
    virtual ~B(){}
};
struct D : B {};
void f(B& obj)
{
    throw obj;
}
void g()
{
    D d;
    try
    {
        f(d);
    }
    catch(const D&)
    {
        std::clog << "D has been catched (can't be catched)" << std::endl;
    }
    catch(const B&)
    {
        std::clog << "B has been catched (always here catched)" << std::endl;
    }
}

EH не работает с динамическими типами объектов. Вместо этого тип исключения проводится через цепочку обработчиков статически на основе типа выражения throw.


Василий 13.11.2023
OPENFILENAME ofn;
memset(&ofn, 0, sizeof(ofn));
// ...
ofn.lpstrFilter = _T("All Files (*.*)\0*.*");

// Правильно: ofn.lpstrFilter = _T("All Files (*.*)\0*.*\0");

В WinAPI есть структура OPENFILENAME, чтобы создавать диалог выбора файла. Так вот, там фильтр задаётся набором строк, разделённых терминальным нулём. А чтобы понять, что список фильтров закончен, надо два терминальных нуля подряд. Я в начале не разобрался и долго тупил, почему то вроде всё хорошо, а как заказчику показывать, то в диалоге мусор какой-то.


alex_bob 13.11.2023
size_t allbits = ~0u;

Всё было хорошо и замечательно, пока это было в 32-битной программе. Было 0xFFFFFFFF. Казалось-бы в 64-битной значение будет 0xFFFFFFFFFFFFFFFF. Реальность: 0x00000000FFFFFFFF. Всё честно, но неожиданно.

close form

Заполните форму в два простых шага ниже:

Ваши контактные данные:

Шаг 1
Поздравляем! У вас есть промокод!

Тип желаемой лицензии:

Шаг 2
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности
close form
Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Бесплатная лицензия PVS‑Studio для специалистов Microsoft MVP
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Мне интересно попробовать плагин на:
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form

Ваш запрос в обработке

Мы пришлем ключ на .

Если вы так и не получили ответ, пожалуйста, проверьте, отфильтровано ли письмо в одну из следующих стандартных папок:

  • Промоакции
  • Оповещения
  • Спам