Pull to refresh

Простой односвязный список на С

Level of difficultyEasy
Reading time6 min
Views2.9K

Всем привет. В 3д помимо моделек - статических, существуют анимации - анимированные модели, которые имеют набор своих данных, эти данные нужны для отображения модельки и её анимирования.

Если выбрать такой язык как С, по каким-либо причинам, рано или поздно можно столкнуться с отсутствием некоторых сущностей. Самой часто используемой сущностью является vector. Если оттолкнуться от того, что простейшая абстракция вектор, и еще чуть упростить можно придти логически к *односвязному списку. Но вот незадача, если пребывать на этом этапе по наименьшему сопротивлению, то придётся на каждую структуру писать свою реализацию это в худшем случае. В этой статье хочу показать как решил вопрос с односвязным списком.

Постановка задачи

**Скелетная анимация - есть моделька,в которой есть сетка - mesh(состоит из полигонов) и скелетная составляющая.

выглядит она например так

Скрытый текст
тестовая прогонка меша и скелетной составляющей не в блендере
тестовая прогонка меша и скелетной составляющей не в блендере

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

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

И окончательное требование - соглашение на 1 сущность 1 тип данных.

Пользуюсь компилятором gcc 14.2 в среде Linux, assimp из репозитория

Задача поставлена, решил её так:

Скрытый текст
//язык С
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(1)

//кастомная тестовая структура
struct EpicLEgendaryNodes_t
{
    char *name;
    int Num;
    float weights;
};
typedef struct EpicLEgendaryNodes_t EpicLEgendaryNodes;

//определение типов
enum typeElements 
{
    INT,
    CHAR,
    DOUBLE,
    FLOAT,
    CHARLINE,
    EPICLEGENDARYNODES
};

//структура списка
typedef struct NodeL_t NodeL;
struct NodeL_t 
{
    enum typeElements T;
    void* data;
    NodeL* Next;
};


//структура хендлера
typedef struct VNodeL_t VNodeL;
struct VNodeL_t
{
  int NodeSz;
  NodeL *head;
};


//определение необходимого минимума функций, которые покроют поставленную задачу
NodeL* getNode(VNodeL *vNodeL,enum typeElements T,void* d);

NodeL* addNodeL(void *d,enum typeElements T,VNodeL *vNodeL);

NodeL* FREENodeL(VNodeL *vNodeL);

NodeL* getByIndex(VNodeL *vNodeL,int Index);

NodeL* setByAfterIndex(VNodeL *vNodeL,int Index,void *d,enum typeElements T);

void printList(VNodeL *vNodeL);


//метод получения ноды - создание
NodeL* getNode(VNodeL *vNodeL,enum typeElements T,void* d) 
{
    char* dd=NULL;
    NodeL* tmp=NULL;
    tmp=malloc(sizeof(NodeL));
    switch(T)
    {
        case INT:
            tmp->data=malloc(sizeof(int));
            memcpy(tmp->data, d, sizeof(int)); //! использую memcpy
            vNodeL->NodeSz++;
            break;
        case CHAR:
            tmp->data=malloc(sizeof(char));
            memcpy(tmp->data, d, sizeof(char));
            vNodeL->NodeSz++;
            break;
        case DOUBLE:
            tmp->data=malloc(sizeof(double));
            memcpy(tmp->data, d, sizeof(double));
            vNodeL->NodeSz++;
            break;
        case FLOAT:
            tmp->data=malloc(sizeof(float));
            memcpy(tmp->data, d, sizeof(float));
            vNodeL->NodeSz++;
            break;
        case CHARLINE:
            dd=d;
            size_t szS=strlen(dd)+1;
            tmp->data=malloc(sizeof(char)*szS);
            ///dd[szS]='\0';
            memcpy(tmp->data, d, sizeof(char)*szS);
            vNodeL->NodeSz++;
            break;
        case EPICLEGENDARYNODES:
            tmp->data=malloc(sizeof(EpicLEgendaryNodes));
            memcpy(tmp->data, d, sizeof(EpicLEgendaryNodes));
            vNodeL->NodeSz++;
            break;
    }
    tmp->Next=NULL;
    return tmp;
}


//добавление в конец ноды
NodeL* addNodeL(void *d,enum typeElements T,VNodeL *vNodeL) 
{
    NodeL* tmp=getNode(vNodeL,T,d);
    if( vNodeL->head == NULL )
    {
        tmp->T=T;
        return tmp;
    }

    NodeL* cur = vNodeL->head;
    while( cur->Next != NULL )cur = cur->Next;

    cur->Next = tmp;

    return vNodeL->head;
}

//очищение структуры списка
NodeL* FREENodeL(VNodeL *vNodeL)
{
    NodeL* cur=vNodeL->head;
    while( cur != NULL )
    {
        NodeL* temp=cur;
        cur = temp->Next;
        free(temp);
    }
  
    return vNodeL->head;
}


//получение значения по индексу
NodeL* getByIndex(VNodeL *vNodeL,int Index)
{
    if(Index>vNodeL->NodeSz-1)
    {
      printf("\n\ngetByIndex\nSearchIndex > SizeNodeLists\n");
      return 0;
    }
  
    int tInd=0;
    NodeL *cur=vNodeL->head;
  
    while(cur!=NULL)
    {
        if(tInd==Index)break;
        tInd++;
        cur=cur->Next;
    }
    return cur;
}


//вставка в место следующее за заданным индексом
NodeL* setByAfterIndex(VNodeL *vNodeL,int Index,void *d,enum typeElements T)
{
    NodeL *setting=getNode(vNodeL,T,d);
    if(Index>vNodeL->NodeSz-1)
    {
      printf("SearchIndex > SizeNodeLists\n");
      return 0;
    }
  
    int tInd=0;
    NodeL *cur=vNodeL->head;
    while(cur!=NULL)
    {
        if(tInd==Index)break;
        tInd++;
        cur=cur->Next;
    }
  
    NodeL *tmp1=cur->Next;
    cur->Next=setting;
    setting->Next=tmp1;
    return vNodeL->head;
}


//вывод списка
void printList(VNodeL *vNodeL)
{
    if(vNodeL->head==NULL){printf("LIST IS EMPTY\n");return ;}
    if(vNodeL==NULL){printf("LIST IS EMPTY\n");return ;}
    NodeL* cur=vNodeL->head;
    enum typeElements Type=vNodeL->head->T;
    
    while(cur!=NULL)
    {
        switch(Type)
        {
            case INT:
                printf("%d\n",*(int*)cur->data);
                break;
            case CHAR:
                printf("%c\n",*(char*)cur->data);
                break;
            case DOUBLE:
                printf("%f\n",*(double*)cur->data);
                break;
            case FLOAT:
                printf("%f\n",*(float*)cur->data);
                break;
            case CHARLINE:
                printf("%s\n",(char*)cur->data);
                break;
            case EPICLEGENDARYNODES:
              {
                EpicLEgendaryNodes *temp=(EpicLEgendaryNodes*)cur->data;
                printf("%s\n",temp->name);
                printf("%d\n",temp->Num);
                printf("%f\n",temp->weights);
                break;
              }
        }
        cur=cur->Next;
    }
}


//типовая прогонка списка 1 тест базовых типов 2 тест кастомной структурки
int main()
{
  //1
    VNodeL *tempBuf=malloc(sizeof(VNodeL));
    tempBuf->head=NULL;
    tempBuf->NodeSz=0;
    int t=4;
    float qq=0.1f;
    double qw=0.1f;
    char* strline=(char*)"CHARLINE";
    tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++;
    strline="CHARLINE1";
    tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++;
    strline="CHARLINE2";
    tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++;
    strline="CHARLINE3";
    tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++;

    printList(tempBuf);

    NodeL* search=getByIndex(tempBuf,4);
    printf("%d\n",tempBuf->NodeSz);

    tempBuf->head=setByAfterIndex(tempBuf,2,"CHARLINE10",CHARLINE);

    search=getByIndex(tempBuf,4);
    printf("\nSearched element\n%s\n\n",(char*)search->data);//см в printList
    tempBuf->head=FREENodeL(tempBuf);
    free(tempBuf);
    //tempBuf->head=NULL;
    //printList(tempBuf);
    
  //2
    VNodeL *tempBuf1=malloc(sizeof(VNodeL));;
    tempBuf1->head=NULL;
    tempBuf1->NodeSz=0;
    EpicLEgendaryNodes temp;
    temp.name="CHARLINE111111";
    temp.Num=t;
    temp.weights=qq;
    tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1);t++;qq++;qw++;
    temp.name="CHARLINE22221";
    temp.Num=t;
    temp.weights=qq;
    tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1);t++;qq++;qw++;
    temp.name="CHARLINE23333";
    temp.Num=t;
    temp.weights=qq;
    tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1);t++;qq++;qw++;
    temp.name="CHARLINE34444";
    temp.Num=t;
    temp.weights=qq;
    tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1);
    printList(tempBuf1);
    tempBuf1->head=FREENodeL(tempBuf1);
    free(tempBuf1);
    //tempBuf1->head=NULL;
    //printList(tempBuf1);
    return 0;
}

Вывод:

CHARLINE
CHARLINE1
CHARLINE2
CHARLINE3


getByIndex
SearchIndex > SizeNodeLists
4

Searched element
CHARLINE3

CHARLINE111111
8
4.100000
CHARLINE22221
9
5.100000
CHARLINE23333
10
6.100000
CHARLINE34444
11
7.100000

gcc -Ofast -ffast-math -flto -msse4.1 main.c -lm

clang -Ofast -ffast-math -flto -msse4.1 main.c -lm

/nologo /MD /O2 /Ob2 /DNDEBUG - x64! msvc v19.40 VS17.10

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

При такой организации, конечно придётся хранить структуры рядом с перечислением.

Дополнительная информация:

*https://ru.wikipedia.org/wiki/Скелетная_анимация

**https://en.wikipedia.org/wiki/Linked_list

Tags:
Hubs:
Total votes 15: ↑3 and ↓12-9
Comments48

Articles