Для получения триального ключа
заполните форму ниже
Team License (базовая версия)
Enterprise License (расширенная версия)
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

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

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

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

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

Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте папку
Spam/Junk и нажмите на письме кнопку "Не спам".
Так Вы не пропустите ответы от нашей команды.

>
>
>
Тем, кто учится программировать и решил…

Тем, кто учится программировать и решил написать вопрос на Stack Overflow: "Почему код не работает?"

28 Июн 2022

На сайте Stack Overflow много вопросов от людей, ещё только изучающих языки программирования. Лайфхак: ответы на многие эти вопросы можно получить сразу, запустив анализатор кода. Получится быстрее.

0959_PVS_education_1_ru/image1.png

Эту заметку меня побудила написать дискуссия "Segmentation fault when converting char * to char **" на сайте Stack Overflow. Человек, изучающий программирование, интересуется, что не так с его кодом.

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>

char **get_words(char *buffer, char delimiter)
{
    printf("buffer = %s\n", buffer);
    char **words = malloc(sizeof(char *) * 100);
    if (words == NULL) {
        printf("Malloc Error\n");
        exit(84);
    }
    for (int i = 0; i < 100; i++) {
        words[i] = malloc(sizeof(char) * 100);
        if (words[i] == NULL) {
            printf("Malloc Error\n");
            exit(84);
        }
    }
    int word_count = 0;
    int l = 0;
    for (int i = 0; buffer[i] != '\0' && buffer[i]  != '\n'; i++, l++) {
        if (buffer[i] == delimiter) {
            words[word_count][l] = '\0';
            word_count++;
            l = -1;
        }
        else
            words[word_count][l] = buffer[i];
    }
    words[word_count][l] = '\0';
    return (words);
}

int main()
{
    char *buffer = malloc(sizeof(char) * 100);
    buffer = "hello world !\n";
    char **words = get_words(buffer, ' ');
    printf("words[0]= %s\n", words[0]);
    free (buffer);
    char **reply = get_words("Second call\n", ' ');
    printf("reply[0] = %s\n", reply[0]);
}

Таких вопросов достаточно на Stack Overflow. И часто на них отвечают медленно и неохотно. Это обоснованно. Согласитесь, не хочется изучать достаточно большой текст программы только ради того, чтобы найти какую-то скучную ошибку. Эти ошибки, как правило, связаны с ещё недостаточным знанием языка, и ответ, скорее всего, сведётся к совету прочитать определённый раздел книги или документации.

Это не снобизм со стороны более опытных разработчиков. Им просто не очень интересно тратить время на то, чтобы разбираться, что не так с лабораторными работами.

Вернёмся к вопросу на Stack Overflow, про который я говорил выше. Вопрос висел уже пару дней, а ответа всё нет. Как человеку продвинуться дальше?

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

Запустим online-версию анализатора PVS-Studio для приведённого в вопросе кода. Первым интересным и важным предупреждением является сообщение: V1031 The 'malloc' function is not declared. Passing data to or from this function can be affected.

Без объявления функции malloc программа уже работает непонятным образом. В языке Си считается, что если функция не объявлена, то она возвращает int. А на самом деле это указатель. Чем это опасно, рассказано в заметке "Красивая 64-битная ошибка на языке Си". Исправим эту проблему, добавив #include <stdlib.h>.

Теперь вывод анализатора изменится, и мы видим следующую серьёзную проблему: 43:1: note: V773 The 'buffer' pointer was assigned values twice without releasing the memory. A memory leak is possible.

Ошибка здесь:

char *buffer = malloc(sizeof(char) * 100);
buffer = "hello world !\n";
....
free (buffer);

Значение указателя перетирается. Чтобы скопировать строку в буфер, нужно использовать специальные функции, например strcpy. Внесём исправления.

Исправленный код:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>

char **get_words(char *buffer, char delimiter)
{
    printf("buffer = %s\n", buffer);
    char **words = malloc(sizeof(char *) * 100);
    if (words == NULL) {
        printf("Malloc Error\n");
        exit(84);
    }
    for (int i = 0; i < 100; i++) {
        words[i] = malloc(sizeof(char) * 100);
        if (words[i] == NULL) {
            printf("Malloc Error\n");
            exit(84);
        }
    }
    int word_count = 0;
    int l = 0;
    for (int i = 0; buffer[i] != '\0' && buffer[i]  != '\n'; i++, l++) {
        if (buffer[i] == delimiter) {
            words[word_count][l] = '\0';
            word_count++;
            l = -1;
        }
        else
            words[word_count][l] = buffer[i];
    }
    words[word_count][l] = '\0';
    return (words);
}

int main()
{
    char *buffer = malloc(sizeof(char) * 100);
    if (buffer == NULL)
        exit(84);
    strcpy(buffer, "hello world !\n");
    char **words = get_words(buffer, ' ');
    printf("words[0]= %s\n", words[0]);
    free (buffer);
    char **reply = get_words("Second call\n", ' ');
    printf("reply[0] = %s\n", reply[0]);
}

Этот код ещё нельзя назвать красивым и безопасным, но он работает. Так что такой способ поиска ошибок вполне может помочь в процессе обучения.

Дополнительные ссылки:

Популярные статьи по теме
Почему в С++ массивы нужно удалять через delete[]

Дата: 27 Июл 2022

Автор: Михаил Гельвих

Заметка рассчитана на начинающих C++ программистов, которым стало интересно, почему везде твердят, что нужно использовать delete[] для массивов, но вместо внятного объяснения – просто прикрываются ма…
Конкурс внимательности: PVS-Studio vs Хакер

Дата: 21 Июл 2022

Автор: Андрей Карпов

Время от времени мы пишем статьи в духе "статический анализатор внимательнее C++ программиста". Сегодня мы продолжим эту традицию, разве что заменив "программист" на "хакер".
Предупреждения помогают писать лаконичный код

Дата: 19 Июл 2022

Автор: Андрей Карпов

Некоторые предупреждения анализатора или компилятора сложно однозначно классифицировать как ложное срабатывание или указание на настоящую ошибку. Бывает, что формально анализатор/компилятор прав, но …
Заводим трактор: QMake -> CMake

Дата: 18 Июл 2022

Автор: Гость

Заводим трактор: переезжаем с QMake на CMake. По дороге заглянем на улицу "Кросс компиляторщиков", в сквер "Систем сборки" и посидим в баре "Управления зависимостями". Заодно увидим тех, кто использу…
Межмодульный анализ C и C++ проектов в деталях. Часть 2

Дата: 14 Июл 2022

Автор: Олег Лысый

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

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

Следующие комментарии
Unicorn with delicious cookie
Мы используем куки, чтобы пользоваться сайтом было удобно. Хотите узнать подробнее?
Принять