← Назад к вопросам

Какие знаешь способы поиска ошибок?

2.2 Middle🔥 191 комментариев
#Исключения и обработка ошибок#Сборка и инструменты

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы поиска ошибок в C/C++

Поиск и исправление ошибок — один из самых критичных навыков в С/C++ разработке. С 10+ лет опыта я использую множество инструментов и методик для выявления проблем на разных уровнях: от простых логических ошибок до сложных race conditions и memory leaks.

1. Статический анализ кода (Static Analysis)

Компилятор Warning Level

// Компиляция с максимальным уровнем предупреждений
// g++ -Wall -Wextra -Wpedantic -Werror file.cpp
// clang++ -Wall -Wextra -Wpedantic -Weverything -Werror file.cpp

// Пример: неиспользуемая переменная
int main() {
    int x = 5;  // Warning: unused variable 'x'
    return 0;
}

Static Analysis Tools

  • Clang Static Analyzer — интегрирован в clang, выявляет логические ошибки
  • Cppcheck — отлично ловит использование undefined behavior
  • Infer — от Facebook, анализирует null pointers и resource leaks
  • Splint — специализирован на безопасности C кода

2. Инструменты динамического анализа (Dynamic Analysis)

AddressSanitizer (ASAN)

// Компиляция с AddressSanitizer
// g++ -fsanitize=address -g main.cpp -o app

#include <iostream>
using namespace std;

int main() {
    int* ptr = new int[5];
    ptr[10] = 42;  // ОШИБКА: buffer overflow
    delete[] ptr;
    delete[] ptr;  // ОШИБКА: double delete
    return 0;
}

// ASAN выведет:
// ERROR: AddressSanitizer: heap-buffer-overflow
// ERROR: AddressSanitizer: attempting double-free

UndefinedBehaviorSanitizer (UBSAN)

// g++ -fsanitize=undefined -g main.cpp -o app

int main() {
    int x = 2147483647;
    x++;  // UB: signed integer overflow
    
    int y = 0;
    int z = 5 / y;  // UB: division by zero
    
    return 0;
}

ThreadSanitizer (TSAN)

// g++ -fsanitize=thread -g main.cpp -o app
// Детектирует race conditions в многопоточном коде

#include <thread>
int shared_var = 0;

void increment() {
    for (int i = 0; i < 1000; ++i) {
        shared_var++;
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    return 0;
}

MemorySanitizer (MSAN)

// g++ -fsanitize=memory -g main.cpp -o app
// Выявляет использование неинициализированной памяти

int main() {
    int x;
    if (x > 5) {  // ОШИБКА: x не инициализирован
        return 1;
    }
    return 0;
}

3. Отладчик GDB

Базовое использование

# Компиляция с debug symbols
g++ -g main.cpp -o app

# Запуск в GDB
gdb ./app

# Основные команды GDB
(gdb) break main              # установить точку останова на main
(gdb) run                     # запустить программу
(gdb) next                    # следующая строка
(gdb) step                    # войти в функцию
(gdb) continue                # продолжить выполнение
(gdb) print variable          # вывести значение переменной
(gdb) backtrace               # вывести стек вызовов
(gdb) frame 0                 # выбрать фрейм
(gdb) info locals             # локальные переменные

4. Valgrind — Инструмент для анализа памяти

# Основной режим: поиск memory leaks
valgrind --leak-check=full --show-leak-kinds=all ./app

# Helgrind: поиск race conditions
valgrind --tool=helgrind ./app

# Massif: профилирование памяти
valgrind --tool=massif ./app
ms_print massif.out.<PID>

# Callgrind: профилирование производительности
valgrind --tool=callgrind ./app
callgrind_annotate callgrind.out.<PID>

5. Профилирование и Performance Analysis

Perf (Linux profiler)

# Профилирование процессорных циклов
perf record ./app
perf report

# Анализ определённой функции
perf stat -e cycles,instructions,cache-misses ./app

Flamegraph

# Создание flame graph для визуализации горячих точек
perf record -F 99 -g ./app
perf script | stackcollapse-perf.pl | flamegraph.pl > graph.svg

6. Unit Testing и Assertion'ы

#include <cassert>
#include <iostream>

void processData(int* ptr, int size) {
    assert(ptr != nullptr);    // Fail fast на ошибке
    assert(size > 0);
}

#include <gtest/gtest.h>

TEST(MyTest, BasicAssertion) {
    EXPECT_EQ(2 + 2, 4);
    EXPECT_NE(nullptr, ptr);
    ASSERT_TRUE(condition);
}

7. Встроенные инструменты IDE

  • CLion — встроенный debugger, thread sanitizers
  • Visual Studio — код analysis, memory analysis
  • VS Code + CodeLLDB — basic debugging

8. Логирование и Трассировка

#include <spdlog/spdlog.h>

int main() {
    auto logger = spdlog::basic_logger_mt("logger", "logs/basic.txt");
    logger->info("Processing started");
    logger->warn("Warning message");
    logger->error("Error occurred");
    logger->info("Value: {}, Status: {}", value, status);
}

// System call tracing
// strace ./app          # трассировка системных вызовов
// ltrace ./app          # трассировка вызовов библиотек

Практический процесс отладки

Шаги:

  1. Воспроизведение — получить consistentный fail case
  2. Изоляция — минимизировать код для воспроизведения
  3. Гипотеза — предположить, где ошибка
  4. Инструменты — выбрать нужный инструмент (ASAN для memory, TSAN для race conditions)
  5. Анализ — посмотреть результаты
  6. Фиксинг — исправить проблему
  7. Проверка — убедиться, что фикс работает

Вывод: Эффективный поиск ошибок требует знания множества инструментов и правильного их выбора для конкретной проблемы. Статический анализ ловит очевидные ошибки, динамический анализ — скрытые проблемы памяти и race conditions, а отладчики помогают разобраться в логике.