На сайте Stack Overflow много вопросов от людей, ещё только изучающих языки программирования. Лайфхак: ответы на многие эти вопросы можно получить сразу, запустив анализатор кода. Получится быстрее.
Эту заметку меня побудила написать дискуссия «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]); }
Этот код ещё нельзя назвать красивым и безопасным, но он работает. Так что такой способ поиска ошибок вполне может помочь в процессе обучения.
Дополнительные ссылки:
- Статический анализ кода.
- PVS-Studio: online версия.
- PVS-Studio: бесплатное использование для студентов.
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov. «Why doesn’t my code work?» — to anyone learning the art of programming and writing to the Stack Overflow community.
ссылка на оригинал статьи https://habr.com/ru/company/pvs-studio/blog/673788/
Добавить комментарий