Пишем простой список на C

от автора

Привет, Хабр!

В статье хочу показать, как написать простой список на C, а именно пародию на ArrayList из Java.

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

Начнем

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

Заголовочные файлы

Чтоб сделать свою программу модульной, то есть разделить код на несколько файлов, а не писать всю программу в одном файле, есть заголовочные файлы. В них содержится информация такие как шаблоны функции, структуры и тд. К каждому заголовочному файлу, нужно сделать файл-реализатор, там уже функции из заголовочного файла будут имплементированы. Если вы раньше изучали Java, то заголовочный файл — это интерфейс, с реализатор это класс который имплементирует этот интерфейс. Также при компиляции нужно указывать реализаторы (Например clang main.c list.c). Подробнее можете посмотреть в ЭТОЙ СТАТЬЕ.

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

#ifndef LIST_H #define LIST_H  typedef struct List {   int* data;   int length;   int capacity; } List;  List* new_list(); void list_add(List* list, int el); int* list_get(List* list, int pos); void list_remove(List* list, int pos); void list_free(List* list);  #endif

List — эта структура списка, data — это указатель на элементы списка, length — это длинна списка, а capacity — это вместимость списка, ну и new_list возвращает указатель на созданный список.

Я думаю нам пока-что хватит этих функций и структуры. Теперь зайдем в list.c. Там подключаем этот заголовочный файл через #include «list.h», и реализуем функции!

Сразу дам код, и буду его объяснять.

Реализация list.h
#include <stdlib.h> #include "list.h" #include <stdio.h>  List* new_list() {   List* list = (List*) malloc(sizeof(List));   if(list == NULL) {     return NULL;   }   list->data = malloc(sizeof(int) * 1);   if (list->data == NULL) {     free(list);     return NULL;   }   list->capacity = 1;   list->length = 0;   return list; }  void list_add(List* list, int el) {   if(list->length == list->capacity) {     list->capacity *= 2;     list->data = realloc(list->data, sizeof(int) * list->capacity);   }   list->data[list->length++] = el; }  int* list_get(List* list, int pos) {   if(pos >= 0 && pos < list->length)     return &list->data[pos];   printf("%s\n", "return NULL");   return NULL; } void list_remove(List* list, int pos) {   if(pos >= 0 && pos < list->length){     for (int i = pos; i < list->length - 1; i++) {       list->data[i] = list->data[i + 1];     }     list->length--;   } }  void list_free(List* list) {   free(list->data);   free(list); }

Объяснение функций

new_list — Тут создается список. Мы выделяем под него память, и если на устройстве не хватило места (malloc вернет NULL) то сразу возвращаем NULL. Далее выделяем память память под data, то есть наши элементы, и если на устройстве не хватило места, то освобождаем память выделенную для объекта list, и возвращаем NULL. Также устанавливаем стандартную вместимость (1), и начальную длину (0).

list_add(List*, int) — Тут добавляем элемент в список, передав список и добавляемый элемент. И если длинна списка будет совпадать с вместимостью, умножаем вместимость вдвое. И с помощью realloc меняем размер выделенной памяти для списка. И после всего этого, в конец добавляем переданный элемент.

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

list_get(List*, int) — возвращает элемент из списка по указанной позиции. В параметры передаем список и позицию элемента. И если позиция будет в промежутке от 0 до длинны списка, то возвращаем элемент, иначе возвращаем NULL.

list_remove(List*, int pos) — удаляем элемент по указанной позиции, идет таже проверка из list_get, и просто сдвигаем элементы после удаленной позиции назад. Далее просто уменьшаем длину списка.

list_free(List*) — освобождает память выделенную под data и сам список. Все просто.

Пример использования

Просто создадим список, добавим элементы, удалим элемент под индексом 1, и выводим все элементы на экран.

Компилируем код — clang main.c list.c, и запускаем ‑./a.out. Может отличаться в зависимости от OC, я использую Linux и компилятор Clang.

#include <stdio.h> #include "list.h"  int main() {   List* list = new_list();   list_add(list, 12);   list_add(list, 83);   list_add(list, 212);   list_add(list, 0);    list_remove(list, 1);    for(int i=0; i<list->length; i++) {     printf("pos: %d = %d\n", i, *list_get(list, i));   }   list_free(list); }
Output: pos: 0 = 12 pos: 1 = 212 pos: 2 = 0

Конец

Статья получилась довольно короткой, до этого я думал что он будет больше… Но всеже я показал как сделать список похожий на ArrayList из Java. Если есть вопросы или поправки пишите в комментариях.

Пока, Хабр!


ссылка на оригинал статьи https://habr.com/ru/articles/857400/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *