Tips på hur man skapar en Map i C

Permalänk

Tips på hur man skapar en Map i C

Jag vill göra en Map i C. Om ni inte vet vad en Map är så är det en dynamisk lista som innehåller objekt där objekten kan variera. I detta fall så får listan INTE vara dynamisk, den ska vara fixerad för jag vet ändå alla element. Men skapandet av mappen får ske dynamiskt. Men användandet måste vara statiskt.

Låt oss säga att vi har en Map i C och vi har ett två parametervärden, index och sub_index. Index är uint16_t och sub_index är uint8_t. Vi säger nu att mappen ser ut så här Map<index, sub_index> i pesudokod.

Problemet är att varje map ska länka till en heltal i from av uint8_t, uint16_t eller uint32_t.
Så om jag tar fram min mapp och säger Map<0x1000, 0x1> så ska jag kunna komma åt t.ex. ett uin32_t värde och sätta det värdet. Eller om jag säger Map<0x1000, 0x0> så kommer jag åt ett uint8_t värde som jag kan sätta eller läsa.

Hur kan jag göra detta i C?
Vad tror ni om denna kod som presenterar tre funktioner? Kan ni göra bättre?
Testa koden här: https://onlinegdb.com/DUij_UV4m

#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> /* Vår map */ struct Map{ uint16_t index; uint8_t sub_index; void* value; struct Map *next_map; }; /* Addera ett nytt värde */ bool add_new_value(struct Map* start, uint16_t index, uint8_t sub_index, void* value){ /* Plocka fram sista mappen som har next_map == NULL */ struct Map* map = start; while(map->next_map != NULL){ /* Men kolla om index och sub_index inte är upptagna */ if(map->index == index && map->sub_index == sub_index) return false; /* OK - Välj nästa mapp */ map = map->next_map; } /* Här är sista mappen */ map->index = index; map->sub_index = sub_index; map->value = value; /* Skapa ny map */ map->next_map = malloc(sizeof(struct Map)); } /* Plocka fram ett värde */ bool bring_value(struct Map* start, uint16_t index, uint8_t sub_index, void* value){ /* Iterera tills vi hittar next_map som är NULL */ struct Map* map = start; while(map->next_map != NULL){ /* Ta fram värdet */ if(map->index == index && map->sub_index == sub_index){ value = map->value; return true; /* Värde hittat */ } /* Hittade inte mappen - Välj nästa mapp */ map = map->next_map; } return false; /* Värde hittades inte */ } /* Plocka fram ett värde */ bool change_value(struct Map* start, uint16_t index, uint8_t sub_index, void* value){ /* Iterera tills vi hittar next_map som är NULL */ struct Map* map = start; while(map->next_map != NULL){ /* Ta fram värdet */ if(map->index == index && map->sub_index == sub_index){ map->value = value; return true; /* Värde applicerat */ } /* Hittade inte mappen - Välj nästa mapp */ map = map->next_map; } return false; /* Värdet kunde inte appliceras */ } int main(){ /* Skapa våran mapp */ struct Map map; /* Addera ett värde */ uint8_t temperatur = 20; bool add_temperatur = add_new_value(&map, 0x1000, 0x0, &temperatur); printf("Added temperatur status: %s . Value = %i\n", add_temperatur ? "true" : "false", temperatur); /* Addera annat värde på samma index */ uint8_t temperatur2 = 40; bool add_temperatur2 = add_new_value(&map, 0x1000, 0x0, &temperatur); printf("Added temperatur status: %s . Value = %i\n", add_temperatur2 ? "true" : "false", temperatur2); /* Ta fram ett värde för variabeln temperatur */ bool bring_temperatur = bring_value(&map, 0x1000, 0x0, &temperatur); printf("Bring temperatur status: %s . Value = %i\n", bring_temperatur ? "true" : "false", temperatur); /* Ta fram ett värde för variabeln temperatur på ett felaktigt index som aldrig har använts */ bring_temperatur = bring_value(&map, 0x1000, 0x1, &temperatur); printf("Bring temperatur status: %s . Value = %i\n", bring_temperatur ? "true" : "false", temperatur); /* Ändra nu temperatur till något annat */ temperatur = 100; bool change_temperatur = change_value(&map, 0x1000, 0x0, &temperatur); printf("Change temperatur status: %s . Value = %i\n", change_temperatur ? "true" : "false", temperatur); /* Ändra nu temperatur till något annat på ett index som inte finns */ temperatur = 200; change_temperatur = change_value(&map, 0x2000, 0x0, &temperatur); printf("Change temperatur status: %s . Value = %i\n", change_temperatur ? "true" : "false", temperatur); /* Addera nu ett nytt värde, med annan datatyp */ uint16_t lenght = 13443; bool add_length = add_new_value(&map, 0x1000, 0x1, &lenght); printf("Add length status: %s . Value = %i\n", add_length ? "true" : "false", lenght); /* Ändra lenght */ lenght = 15000; bool change_length = change_value(&map, 0x1000, 0x1, &lenght); printf("Change length status: %s . Value = %i\n", change_length ? "true" : "false", lenght); /* Plocka fram length */ bool bring_length = bring_value(&map, 0x1000, 0x1, &lenght); printf("Bring length status: %s . Value = %i\n", bring_length ? "true" : "false", lenght); return 0; }

Permalänk
Medlem

Du har implementerat en länkad lista. Den har som bekant tidskomplexiteten O(n).

En tvådimensionell array enligt din spec skulle ta upp storleken av ca 4,2M element och har tidskomplexiteten O(k). Den går eventuellt att kapa ner storleken om du vet att du bara kommer använda vissa index/subindex.

Mellantinget är en hashtabell.

Valet bör beror på fyllnadsgraden som du förväntar dig.

Permalänk
Skrivet av KAD:

Du har implementerat en länkad lista. Den har som bekant tidskomplexiteten O(n).

En tvådimensionell array enligt din spec skulle ta upp storleken av ca 4,2M element och har tidskomplexiteten O(k). Den går eventuellt att kapa ner storleken om du vet att du bara kommer använda vissa index/subindex.

Mellantinget är en hashtabell.

Valet bör beror på fyllnadsgraden som du förväntar dig.

Jag har faktiskt ALDRIG någonsin gjort en länkad lista. Detta är absolut fösta gången jag tar mig och studerar en sådan. Jag gick dock en C kurs för några år sedan, utan att ens kunna länkad lista. Läraren jag hade kunde inte ens undervisa. Men Google kunde

Jag är faktiskt väldigt nöjd med denna lista och jag kommer inte använda så många index i denna lista. Ca 30 stycken bara.

Men jag stötte faktiskt på ett problem nu som jag skulle vilja ha hjälp med.
Om jag ska lägga in ett värde när jag befinner mig inuti en funktion. Då får jag ett problem trots att jag allokerar. Hur ska jag göra här?

void min_function(struct Map* map){ uint8_t* temperatur = malloc(sizeof(uint8_t)); *temperatur = 20; bool add_temperatur = add_new_value(map, 0x1000, 0x0, temperatur); } int main(){ /* Skapa våran mapp */ struct Map map; /* Lägg in ett värde */ min_function(&map); /* Om jag nu plockar fram mitt värde */ printf("value = %i", *((uint8_t*) map.value)); return 0; }

--------------------
Jag tror jag löste det. Vad tycker ni?

#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> typedef enum { DATA_TYPE_U8, DATA_TYPE_U16, DATA_TYPE_U32 }DATA_TYPE; /* Vår map */ struct Map{ uint16_t index; uint8_t sub_index; void* value; struct Map *next_map; }; /* Addera ett nytt värde */ bool add_new_value(struct Map* start, uint16_t index, uint8_t sub_index, DATA_TYPE data_type){ /* Plocka fram sista mappen som har next_map == NULL */ struct Map* map = start; while(map->next_map != NULL){ /* Men kolla om index och sub_index inte är upptagna */ if(map->index == index && map->sub_index == sub_index) return false; /* OK - Välj nästa mapp */ map = map->next_map; } /* Här är sista mappen */ if(data_type == DATA_TYPE_U8) map->value = (uint8_t*)malloc(sizeof(uint8_t)); else if(data_type == DATA_TYPE_U16) map->value = (uint16_t*)malloc(sizeof(uint16_t)); else if(data_type == DATA_TYPE_U32) map->value = (uint32_t*)malloc(sizeof(uint32_t)); else return false; map->index = index; map->sub_index = sub_index; /* Skapa ny map */ map->next_map = (struct Map*)malloc(sizeof(struct Map)); } /* Plocka fram ett värde */ bool bring_value(struct Map* start, uint16_t index, uint8_t sub_index, void* value){ /* Iterera tills vi hittar next_map som är NULL */ struct Map* map = start; while(map->next_map != NULL){ /* Ta fram värdet */ if(map->index == index && map->sub_index == sub_index){ value = map->value; return true; /* Värde hittat */ } /* Hittade inte mappen - Välj nästa mapp */ map = map->next_map; } return false; /* Värde hittades inte */ } /* Plocka fram ett värde */ bool change_value(struct Map* start, uint16_t index, uint8_t sub_index, void* value){ /* Iterera tills vi hittar next_map som är NULL */ struct Map* map = start; while(map->next_map != NULL){ /* Ta fram värdet */ if(map->index == index && map->sub_index == sub_index){ map->value = value; return true; /* Värde applicerat */ } /* Hittade inte mappen - Välj nästa mapp */ map = map->next_map; } return false; /* Värdet kunde inte appliceras */ } int main(){ /* Skapa våran mapp */ struct Map map; /* Addera ett värde */ uint8_t temperatur = 20; bool add_temperatur = add_new_value(&map, 0x1000, 0x0, DATA_TYPE_U8); printf("Added temperatur status: %s\n", add_temperatur ? "true" : "false"); /* Addera annat värde på samma index */ uint8_t temperatur2 = 40; bool add_temperatur2 = add_new_value(&map, 0x1000, 0x0, DATA_TYPE_U8); printf("Added temperatur status: %s\n", add_temperatur2 ? "true" : "false"); /* Ta fram ett värde för variabeln temperatur */ bool bring_temperatur = bring_value(&map, 0x1000, 0x0, &temperatur); printf("Bring temperatur status: %s . Value = %i\n", bring_temperatur ? "true" : "false", temperatur); /* Ta fram ett värde för variabeln temperatur på ett felaktigt index som aldrig har använts */ bring_temperatur = bring_value(&map, 0x1000, 0x1, &temperatur); printf("Bring temperatur status: %s . Value = %i\n", bring_temperatur ? "true" : "false", temperatur); /* Ändra nu temperatur till något annat */ temperatur = 100; bool change_temperatur = change_value(&map, 0x1000, 0x0, &temperatur); printf("Change temperatur status: %s . Value = %i\n", change_temperatur ? "true" : "false", temperatur); /* Ändra nu temperatur till något annat på ett index som inte finns */ temperatur = 200; change_temperatur = change_value(&map, 0x2000, 0x0, &temperatur); printf("Change temperatur status: %s . Value = %i\n", change_temperatur ? "true" : "false", temperatur); /* Addera nu ett nytt värde, med annan datatyp */ bool add_length = add_new_value(&map, 0x1000, 0x1, DATA_TYPE_U16); printf("Add length status: %s\n", add_length ? "true" : "false"); /* Ändra lenght */ uint16_t lenght = 15000; bool change_length = change_value(&map, 0x1000, 0x1, &lenght); printf("Change length status: %s . Value = %i\n", change_length ? "true" : "false", lenght); /* Plocka fram length */ bool bring_length = bring_value(&map, 0x1000, 0x1, &lenght); printf("Bring length status: %s . Value = %i\n", bring_length ? "true" : "false", lenght); return 0; }

Permalänk

Skall du verkligen hålla på och allokera dina värden dynamiskt? Utrymmet som void-pekaren tar räcker för att spara ditt 8-, 16- eller 32-bitsvärde. Då slipper du dessutom problem när du skickar in adressen till en stackvariabel.

Permalänk
Skrivet av Ingetledigtnamn:

Skall du verkligen hålla på och allokera dina värden dynamiskt? Utrymmet som void-pekaren tar räcker för att spara ditt 8-, 16- eller 32-bitsvärde. Då slipper du dessutom problem när du skickar in adressen till en stackvariabel.

Hur hade du gjort? Kan du visa?
Du menar att man kan göra så här?

if(data_type == DATA_TYPE_U8) map->value = (uint8_t*)0; else if(data_type == DATA_TYPE_U16) map->value = (uint16_t*)0; else if(data_type == DATA_TYPE_U32) map->value = (uint32_t*)0; else

Eller menar du att jag kan ta bort koden helt?

Permalänk

Jag kan bryta ned problemet lite. Varför fungerar detta inte?

#include <stdio.h> #include <stdint.h> #include <stdlib.h> struct Map{ void * value; }; void create_value(struct Map* map){ map->value = (uint8_t*)malloc(sizeof(uint8_t)); } void set_value(struct Map *map, uint32_t value){ map->value = &value; printf("Set: map->value = %i\n", *((uint8_t*) map->value)); // Detta är korrekt (jag vet att pekaren kommer försvinna när jag lämnar funktionen) } void get_value(struct Map *map, void* value){ printf("Get: map->value = %i\n", *((uint8_t*) map->value)); // Hur kan man göra så att värdet behålls? value = map->value; } int main(){ /* Skapa struktur */ struct Map map = {0}; /* Sätt minne till value */ create_value(&map); /* Sätt value */ set_value(&map, 230); /* Hämta value */ uint32_t value; get_value(&map, &value); /* Skriv ut value */ printf("value = %i", value); // Det här blir riktigt fel }

Permalänk
Skrivet av Dew87:

Har du koll på vad en Map är? I C++ vilket bör vara samma som C även om implementationen skiljer sig så är en map en sorterad lista av par av variabler, key value och mapped value. Key values ska vara unika och är då kopplade till ett mapped value. https://www.cplusplus.com/reference/map/map/

Jag måste göra något mer minnessnålt. Så jag kommer använda en hel del pekare.

Permalänk
Medlem

Om du vill ha en odynamisk lista så ska det väl ändå vara en array (på stacken om du ska va snål) och om du vill ha olika typer som du bestämmer vid compile time kan du anävnda unions.

är det en skoluppgift eller något eget du gör? en länkad lista är fruktansvärt dåliga och datorer hatar dom, förutom i skolan, där är det bra uppgifter.

Permalänk
Medlem
Skrivet av heretic16:

Hur hade du gjort? Kan du visa?
Du menar att man kan göra så här?

if(data_type == DATA_TYPE_U8) map->value = (uint8_t*)0; else if(data_type == DATA_TYPE_U16) map->value = (uint16_t*)0; else if(data_type == DATA_TYPE_U32) map->value = (uint32_t*)0; else

Eller menar du att jag kan ta bort koden helt?

Du skulle kunna lagra alltid största numret (32 bitar) och sedan göra bitwise AND när du vill agera på en del av int (8 / 16). Du kan ju då skicka dina int-ar "by value" och har inget behov av pekare egentligen.

struct Map { // ...andra fält... uint32_t value; }

Permalänk
Skrivet av Frappee:

Om du vill ha en odynamisk lista så ska det väl ändå vara en array (på stacken om du ska va snål) och om du vill ha olika typer som du bestämmer vid compile time kan du anävnda unions.

är det en skoluppgift eller något eget du gör? en länkad lista är fruktansvärt dåliga och datorer hatar dom, förutom i skolan, där är det bra uppgifter.

Menar du så här: Titta på uint8_t* value i strukturen.

Det är ingen skoluppgift. Det är något jag själv håller på med.

Skrivet av toj_ts:

Du skulle kunna lagra alltid största numret (32 bitar) och sedan göra bitwise AND när du vill agera på en del av int (8 / 16). Du kan ju då skicka dina int-ar "by value" och har inget behov av pekare egentligen.

struct Map { // ...andra fält... uint32_t value; }

Jag har löst detta med att använda en array för 8-bitar.

Se här.

Jag har alltså en sätt-funktion som använder uint32_t argumentet.
Jag ville först ha void * som argument, men det gick lite dåligt för mig för då måste jag använda pekare.

Jag ville ha en void * som argument på hämta-funktionen. Men att skicka något värde till en datatyp som har void * gick inte heller.

Så jag löste det igenom att använda en uint8_t* array på strukturen och jag använder union för att konvertera uint8_t* arrayen till uint32_t, uint16_t, uint8_t.

Detta är alltså en "MAP" som fungerar som Map<index, sub_index> och man kan sätta olika värden, allt från uint8_t till uint32_t. index går från 0x0 till 0xFFFF, och sub_index går från 0x0 till 0xFF.

Kör koden här: https://onlinegdb.com/kU0AdK1Qk

#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <string.h> typedef enum{ DO_ACCESS_READ, DO_ACCESS_WRITE, DO_ACCESS_READ_WRITE }DO_ACCESS; typedef enum { DO_DATA_TYPE_U8, DO_DATA_TYPE_U16, DO_DATA_TYPE_U32 }DO_DATA_TYPE; /* C standard library */ #include <stdlib.h> #include <stdint.h> #include <stdbool.h> union{ uint8_t array_u8[4]; uint32_t value_u32; uint16_t value_u16; uint8_t value_u8; }convert; /* Our dictionary object */ struct Dictionary_object{ uint16_t index; uint8_t sub_index; DO_ACCESS access; DO_DATA_TYPE data_type; /* Data type */ uint8_t* value; /* Unsigned value array */ struct Dictionary_object *next_dictionary_object; /* This is a linked list */ }; /* Create a new value inside the dictionary object */ bool add_dictionary_object_index(struct Dictionary_object* start, uint16_t index, uint8_t sub_index, DO_DATA_TYPE data_type, DO_ACCESS access){ /* Iterate until we find a null next dictionary object or can add a value */ struct Dictionary_object* dictionary_object = start; while(dictionary_object->next_dictionary_object != NULL){ /* Check if the index and sub index is already used */ if(dictionary_object->index == index && dictionary_object->sub_index == sub_index) return false; /* OK - Choose next dictionary object */ dictionary_object = dictionary_object->next_dictionary_object; } /* Here is the last dictionary object */ if(data_type == DO_DATA_TYPE_U8) dictionary_object->value = (uint8_t*)malloc(sizeof(uint8_t)); else if(data_type == DO_DATA_TYPE_U16) dictionary_object->value = (uint8_t*)malloc(sizeof(uint16_t)); else if(data_type == DO_DATA_TYPE_U32) dictionary_object->value = (uint8_t*)malloc(sizeof(uint32_t)); else return false; /* Wrong data type */ dictionary_object->index = index; dictionary_object->sub_index = sub_index; dictionary_object->access = access; dictionary_object->data_type = data_type; /* Create new dictionary object */ dictionary_object->next_dictionary_object = malloc(sizeof(struct Dictionary_object)); return true; } /* Get the dictionary object value */ bool get_dictionary_object_value(struct Dictionary_object* start, uint16_t index, uint8_t sub_index, uint32_t* value){ /* Iterate until we find a null next dictionary object or can get a value */ struct Dictionary_object* dictionary_object = start; while(dictionary_object->next_dictionary_object != NULL){ /* Check if it's correct index and sub index */ if(dictionary_object->index == index && dictionary_object->sub_index == sub_index){ /* Check if we have read access */ if(dictionary_object->access == DO_ACCESS_READ || dictionary_object->access == DO_ACCESS_READ_WRITE){ if(dictionary_object->data_type == DO_DATA_TYPE_U8){ memcpy(convert.array_u8, dictionary_object->value, 1); *value = convert.value_u8; }else if(dictionary_object->data_type == DO_DATA_TYPE_U16){ memcpy(convert.array_u8, dictionary_object->value, 2); *value = convert.value_u16; }else if(dictionary_object->data_type == DO_DATA_TYPE_U32){ memcpy(convert.array_u8, dictionary_object->value, 4); *value = convert.value_u32; }else{ return false; /* Wrong data type */ } return true; /* You got the value */ } return false; /* Value could not be found due to the reading access */ } /* Could not find the dictionary object - Choose next dictionary object */ dictionary_object = dictionary_object->next_dictionary_object; } return false; /* The dictionary object value could not be found */ } /* Change dictionary object value */ bool set_dictionary_object_value(struct Dictionary_object* start, uint16_t index, uint8_t sub_index, uint32_t value){ /* Iterate until we find a null next dictionary object or can apply a value */ struct Dictionary_object* dictionary_object = start; while(dictionary_object->next_dictionary_object != NULL){ /* Check if it's correct index and sub index */ if(dictionary_object->index == index && dictionary_object->sub_index == sub_index){ /* Check if we have write access */ if(dictionary_object->access == DO_ACCESS_WRITE || dictionary_object->access == DO_ACCESS_READ_WRITE){ if(dictionary_object->data_type == DO_DATA_TYPE_U8){ convert.value_u8 = value; memcpy(dictionary_object->value, convert.array_u8, 1); }else if(dictionary_object->data_type == DO_DATA_TYPE_U16){ convert.value_u16 = value; memcpy(dictionary_object->value, convert.array_u8, 2); }else if(dictionary_object->data_type == DO_DATA_TYPE_U32){ convert.value_u32 = value; memcpy(dictionary_object->value, convert.array_u8, 4); }else{ return false; /* Wrong data type */ } return true; /* The value is set */ } return false; /* The value cannot be set due to writing access */ } /* Could not find the dictionary object - Choose next dictionary object */ dictionary_object = dictionary_object->next_dictionary_object; } return false; /* The value could not be set */ } int main(){ /* Skapa våran mapp */ struct Dictionary_object dictionary_object = {0}; /* Create dictionary object */ bool add_status = add_dictionary_object_index(&dictionary_object, 0x1000, 0x0, DO_DATA_TYPE_U8, DO_ACCESS_READ_WRITE); printf("add_status = %s\n", add_status ? "true" : "false"); /* Sätt ett värde */ bool set_status = set_dictionary_object_value(&dictionary_object, 0x1000, 0x0, 234); printf("set_status = %s\n", set_status ? "true" : "false"); /* Läs av värdet */ uint32_t value; bool get_status = get_dictionary_object_value(&dictionary_object, 0X1000, 0x0, &value); printf("get_status = %s, %i\n", get_status ? "true" : "false", value); return 0; }

Permalänk
Medlem
Skrivet av heretic16:

Jag kan bryta ned problemet lite. Varför fungerar detta inte?

#include <stdio.h> #include <stdint.h> #include <stdlib.h> struct Map{ void * value; }; void create_value(struct Map* map){ map->value = (uint8_t*)malloc(sizeof(uint8_t)); } void set_value(struct Map *map, uint32_t value){ map->value = &value; printf("Set: map->value = %i\n", *((uint8_t*) map->value)); // Detta är korrekt (jag vet att pekaren kommer försvinna när jag lämnar funktionen) } void get_value(struct Map *map, void* value){ printf("Get: map->value = %i\n", *((uint8_t*) map->value)); // Hur kan man göra så att värdet behålls? value = map->value; } int main(){ /* Skapa struktur */ struct Map map = {0}; /* Sätt minne till value */ create_value(&map); /* Sätt value */ set_value(&map, 230); /* Hämta value */ uint32_t value; get_value(&map, &value); /* Skriv ut value */ printf("value = %i", value); // Det här blir riktigt fel }

#include <stdio.h> #include <stdint.h> #include <stdlib.h> struct Map{ void * value; //strukten innehåller en pekare, använder därmed 8byte minne på ett 64bit OS (4byte på 32bit OS) }; void create_value(struct Map* map){ map->value = (uint8_t*)malloc(sizeof(uint8_t)); //Allokera 1byte minne dynamiskt, lagra addressen i den 8byte stora pekaren } void set_value(struct Map *map, uint32_t value){ /* * Pekar om value pekaren till addressen av den temporära stack-variabeln value. * Dels ger detta en minnesläcka, för addressen som du malloc:ade i create_value är nu överskriven, så du kan inte köra free() senare, * men det är också ett programfel eftersom &value är en pekare till ett värde på stacken, som bara är giltlig inne i funktionen. När funktionen avslutar så kommer värdet som addressen pekar på vara odefinerat. * * Att du sen blandat in ett 4 byte (uint32_t) värde till en 8 byte pekare med 1 byte allokerat minne.. Det blir mest bara fel. */ //map->value = &value; /* * Det du egentligen skulle behövt göra är att spara värdet av det man skickar in till addressen som redan är lagrad. * Men att du blandat uint8 och uint32 typerna förvirrar saker.. */ *(uint8_t*)map->value=(uint8_t)value; printf("Set: map->value = %i\n", *((uint8_t*) map->value));//skriver ut 230 } void get_value(struct Map *map, void* value){ printf("Get: map->value = %i\n", *((uint8_t*) map->value)); //skriver ut 230 /* * Sparar addressen som är lagrad i map->value (som pekar nånstans på stacken iom set_value) i den lokala stack variabeln value, * eftersom value-variabeln försvinner när funktionen avslutar och du inte använder den ytterligare så gör detta ingenting. */ //value = map->value; /* * Det du du istället vill göra är att spara värdet av det som map->value pekar på till platsen som value pekar på. * * Här är dock problemet att vid en vanlig tilldelning, såsom ex: * uint32_t a=3; * uint32_t b=a; * * Så vet programmet att den vid b=a raden måste kopiera 4 byte från minnet där a ligger till minnet där b ligger, för båda sidorna i det fallet är uint32_t (32bit=4byte). * * Eftersom du använder void* så finns dock ingen storleksinformation, utan du måste explicit ange hur många byte du läser, och hur många byte du skriver, * så map->value måste typecastas till en uint8_t pekare, och sen dereferenseras till ett 1byte värde, * och eftersom value pekaren i ditt fall är en uint32_t i main() så behöver du typecasta skrivningen till en uint32_t pekare som sen dereferenseras. * * Rent lagringsmässigt hade det gått att cast:a value till uint8_t pekare också, men då hade den bara skrivit 1 byte (och övriga 3 byte i int:en skulle ha kvar det värde de hade sedan innan, * samt även om övriga byte redan råkade vara 0 så skulle little/big-endian ett problem här). */ *(uint32_t*)value = *(uint8_t*)map->value; } int main(){ struct Map map = {0}; create_value(&map); set_value(&map, 230); uint32_t value; get_value(&map, &value); printf("value = %i\n", value);//skriver ut 230 }

Visa signatur

The difference between stupidity and genius - the latter has limits

Permalänk
Skrivet av Zevon:

#include <stdio.h> #include <stdint.h> #include <stdlib.h> struct Map{ void * value; //strukten innehåller en pekare, använder därmed 8byte minne på ett 64bit OS (4byte på 32bit OS) }; void create_value(struct Map* map){ map->value = (uint8_t*)malloc(sizeof(uint8_t)); //Allokera 1byte minne dynamiskt, lagra addressen i den 8byte stora pekaren } void set_value(struct Map *map, uint32_t value){ /* * Pekar om value pekaren till addressen av den temporära stack-variabeln value. * Dels ger detta en minnesläcka, för addressen som du malloc:ade i create_value är nu överskriven, så du kan inte köra free() senare, * men det är också ett programfel eftersom &value är en pekare till ett värde på stacken, som bara är giltlig inne i funktionen. När funktionen avslutar så kommer värdet som addressen pekar på vara odefinerat. * * Att du sen blandat in ett 4 byte (uint32_t) värde till en 8 byte pekare med 1 byte allokerat minne.. Det blir mest bara fel. */ //map->value = &value; /* * Det du egentligen skulle behövt göra är att spara värdet av det man skickar in till addressen som redan är lagrad. * Men att du blandat uint8 och uint32 typerna förvirrar saker.. */ *(uint8_t*)map->value=(uint8_t)value; printf("Set: map->value = %i\n", *((uint8_t*) map->value));//skriver ut 230 } void get_value(struct Map *map, void* value){ printf("Get: map->value = %i\n", *((uint8_t*) map->value)); //skriver ut 230 /* * Sparar addressen som är lagrad i map->value (som pekar nånstans på stacken iom set_value) i den lokala stack variabeln value, * eftersom value-variabeln försvinner när funktionen avslutar och du inte använder den ytterligare så gör detta ingenting. */ //value = map->value; /* * Det du du istället vill göra är att spara värdet av det som map->value pekar på till platsen som value pekar på. * * Här är dock problemet att vid en vanlig tilldelning, såsom ex: * uint32_t a=3; * uint32_t b=a; * * Så vet programmet att den vid b=a raden måste kopiera 4 byte från minnet där a ligger till minnet där b ligger, för båda sidorna i det fallet är uint32_t (32bit=4byte). * * Eftersom du använder void* så finns dock ingen storleksinformation, utan du måste explicit ange hur många byte du läser, och hur många byte du skriver, * så map->value måste typecastas till en uint8_t pekare, och sen dereferenseras till ett 1byte värde, * och eftersom value pekaren i ditt fall är en uint32_t i main() så behöver du typecasta skrivningen till en uint32_t pekare som sen dereferenseras. * * Rent lagringsmässigt hade det gått att cast:a value till uint8_t pekare också, men då hade den bara skrivit 1 byte (och övriga 3 byte i int:en skulle ha kvar det värde de hade sedan innan, * samt även om övriga byte redan råkade vara 0 så skulle little/big-endian ett problem här). */ *(uint32_t*)value = *(uint8_t*)map->value; } int main(){ struct Map map = {0}; create_value(&map); set_value(&map, 230); uint32_t value; get_value(&map, &value); printf("value = %i\n", value);//skriver ut 230 }

Attans! Jag hade totalt glömt bort att man kunde kasta på vänstra ledet också. Det var nog därför det inte fungerade för mig.

*(uint32_t*)value = *(uint8_t*)map->value; *(uint8_t*)map->value=(uint8_t)value;

Men om en void* innehåller 8 byte minne på 64-bit OS och 4 byte minne på 32-bit OS, så tjänar jag på att göra som jag gjorde ovan. Se denna kod.

Permalänk

Om du nu skall stoppa in ett 8-, 16- eller 32-bitarsvärde i din map/länkade lista så räcker det att ha plats för en uint32_t. Den kommer rymma alla tre storlekarna. Jobba i uint32_t hela tiden så slipper du en massa switchar och skit. Det finns ingen anledning att försöka hålla isär dem när du skrivit dem till structen. Samma sak när du skall stoppa in ett värde, låt funktionen som gör det ta en uint32_t och låt kompilatorn casta upp de mindre värdena till unit32_t. Värdet kommer bevaras.

Om du vet att du bara kommer ha ett fåtal argument och att du bara kommer stoppa in data en gång, gör livet lätt för dig och ha dina data i en sorterad tabell. Du slipper dynamiskt minne och kan binärsöka i tabellen. Det kommer gå mycket fortare än att traska runt i länkade listor.

Permalänk
Medlem
Skrivet av heretic16:

Attans! Jag hade totalt glömt bort att man kunde kasta på vänstra ledet också. Det var nog därför det inte fungerade för mig.

*(uint32_t*)value = *(uint8_t*)map->value; *(uint8_t*)map->value=(uint8_t)value;

Men om en void* innehåller 8 byte minne på 64-bit OS och 4 byte minne på 32-bit OS, så tjänar jag på att göra som jag gjorde ovan. Se denna kod.

Alla pekare, oavsätt om det är void*, uint8_t*, uint32_t*, .. är 8 byte på 64bit OS. En pekare behöver lagra en minnesaddress, och för att addressera på en 64bit dator så behövs 64bit lagring. Typen av en pekare berättar bara hur många byte:s värdet som lagras på pekaraddressen är.

struct Dictionary_object{ uint16_t index; //2byte uint8_t sub_index; //1byte DO_ACCESS access; //4byte (enum=int) DO_DATA_TYPE data_type; //4byte (enum=int) uint8_t* value;//8byte för pekaren, sen ytterligare 1-4 byte som du senare allokerar med malloc för att lagra det faktiska värdet. struct Dictionary_object *next_dictionary_object; //8byte };

Om ditt value bara skulle vara "uint32_t value" (dvs inte pekare) så skulle du slippa köra malloc för den, samt skulle du bara använda 4 byte istället för 17-20 bytes per värde (dvs pekare + allokerat minne för värdet + 2 enum som berättar hur värdet kan adresseras).

Så du tjänar inte något i minnesanvändning att göra som du gör, du har istället lagt på en stor overhead (plus att koden blir svårläst och onödvändigt komplex).

Visa signatur

The difference between stupidity and genius - the latter has limits

Permalänk
Skrivet av Ingetledigtnamn:

Om du nu skall stoppa in ett 8-, 16- eller 32-bitarsvärde i din map/länkade lista så räcker det att ha plats för en uint32_t. Den kommer rymma alla tre storlekarna. Jobba i uint32_t hela tiden så slipper du en massa switchar och skit. Det finns ingen anledning att försöka hålla isär dem när du skrivit dem till structen. Samma sak när du skall stoppa in ett värde, låt funktionen som gör det ta en uint32_t och låt kompilatorn casta upp de mindre värdena till unit32_t. Värdet kommer bevaras.

Om du vet att du bara kommer ha ett fåtal argument och att du bara kommer stoppa in data en gång, gör livet lätt för dig och ha dina data i en sorterad tabell. Du slipper dynamiskt minne och kan binärsöka i tabellen. Det kommer gå mycket fortare än att traska runt i länkade listor.

Så kan jag göra. Men uint32_t tar ju upp 4 bytes i strukten. Medan uint8_t tar upp 1 byte endast.

Problemet är att jag vet inte exakt hur många argument jag kommer använda. Jag vet att jag kommer skapa mappen bara en gång. Sedan inget mer.
Men jag vet inte hur jag ska kunna använda binärsökning i strukrurer?

Permalänk
Skrivet av Zevon:

Alla pekare, oavsätt om det är void*, uint8_t*, uint32_t*, .. är 8 byte på 64bit OS. En pekare behöver lagra en minnesaddress, och för att addressera på en 64bit dator så behövs 64bit lagring. Typen av en pekare berättar bara hur många byte:s värdet som lagras på pekaraddressen är.

struct Dictionary_object{ uint16_t index; //2byte uint8_t sub_index; //1byte DO_ACCESS access; //4byte (enum=int) DO_DATA_TYPE data_type; //4byte (enum=int) uint8_t* value;//8byte för pekaren, sen ytterligare 1-4 byte som du senare allokerar med malloc för att lagra det faktiska värdet. struct Dictionary_object *next_dictionary_object; //8byte };

Om ditt value bara skulle vara "uint32_t value" (dvs inte pekare) så skulle du slippa köra malloc för den, samt skulle du bara använda 4 byte istället för 17-20 bytes per värde (dvs pekare + allokerat minne för värdet + 2 enum som berättar hur värdet kan adresseras).

Så du tjänar inte något i minnesanvändning att göra som du gör, du har istället lagt på en stor overhead (plus att koden blir svårläst och onödvändigt komplex).

Du har helt rätt. Jag måste förenkla.

Men om en pekare tar 8 bytes + om pekaren har ytterligare 4 bytes (uint32_t) så blir det 12 bytes. Då är det lika bra som du säger att bättre att jag använder uint32_t istället för void *.

Se här. Nu är det ännu mera förenklat. Tack.
https://onlinegdb.com/yYF1rPnSf

Men jag måste ändå ha malloc om jag vill ha denna typ av länkad lista?

Kan jag göra en snabbare sökning med binärsökning som någon ovan sade?

Jag vet slut-adressen på min struktur och början-adressen på min struktur.
Då kan jag hitta mitten-adressen igenom en while-loop.

Men om jag två variabler att söka med, då blir det svårare.

Men att använda binärsökning med länkade listor är väll rätt så offektivt? Man måste ändå söka sig till mitten igenom iterationer.
Här är ett exempel på där två while-loopar används för binär sökning i länkad lista.
https://www.geeksforgeeks.org/binary-search-on-singly-linked-...

En while-loop för att hitta mitten noden och en while-loop för att avgöra om man ska gå ett steg åt höger eller ett steg åt vänster.
Det blir mycket interation här.

Permalänk
Skrivet av heretic16:

Du har helt rätt. Jag måste förenkla.

Se här. Nu är det ännu mera förenklat. Tack.
https://onlinegdb.com/yYF1rPnSf

Att skippa storlekshanteringen förenklade koden, fint.

Om du vill göra koden ännu enklare kan du slå ihop index och sub_index till ett värde:

unit32_t key(uint16_t index, uint8_t sub_index) { return index << 16 | sub_index; }

Då kan du göra båda jämförelserna i en 32-bitsoperation.

Nästa steg är att inte köra en länkad lista. Den länkade listan är en ineffektiv datastruktur då du måste linjärsöka för att hitta dit data och du får en del overhead i samband med malloc. Stoppa dina DictionaryObject i en tabell (array) istället. Då blir du av med next-pekaren. Du kan antingen ha en "tillräckligt" stor array från början eller allokera den dynamiskt och göra en realloc för att göra den större när den blir full. När du har ditt data i en sorterad tabell, då kan du binärsöka. När du binärsöker i en tabell där varje element består av nyckel + värde så söker du bara på nyckeln, inte hela strukturen.

Permalänk
Datavetare
Skrivet av heretic16:

Kan jag göra en snabbare sökning med binärsökning som någon ovan sade?

Jag vet slut-adressen på min struktur och början-adressen på min struktur.
Då kan jag hitta mitten-adressen igenom en while-loop.

Men om jag två variabler att söka med, då blir det svårare.

Men att använda binärsökning med länkade listor är väll rätt så offektivt? Man måste ändå söka sig till mitten igenom iterationer.
Här är ett exempel på där två while-loopar används för binär sökning i länkad lista.
https://www.geeksforgeeks.org/binary-search-on-singly-linked-...

En while-loop för att hitta mitten noden och en while-loop för att avgöra om man ska gå ett steg åt höger eller ett steg åt vänster.
Det blir mycket interation här.

Binärsökning blir lite poänglöst om man har en länkad lista, i det läget är det snabbare att linjärsöka.

C har ju inbyggt stöd för både sortering och binärsökning, detta är en möjlighet (förutsätter C99)

#include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // Generic C map with O(log(N)) search complexity typedef int (*map_compar_fn)(const void *, const void *); typedef struct map_st { bool is_sorted; map_compar_fn compar_fn; size_t memb_sz; size_t memb_cnt; void *memb; } map_t; map_t* map_new(size_t memb_sz, map_compar_fn compar_fn) { map_t *map = calloc(1, sizeof(map_t)); map->memb_sz = memb_sz; map->compar_fn = compar_fn; return map; } void map_insert(map_t *map, void *item) { map->memb_cnt += 1; map->memb = realloc(map->memb, map->memb_cnt * map->memb_sz); memcpy((char*)map->memb + (map->memb_cnt - 1) * map->memb_sz, item, map->memb_sz); map->is_sorted = false; } void* map_get(map_t *map, const void *key) { if (!map->is_sorted) { qsort(map->memb, map->memb_cnt, map->memb_sz, map->compar_fn); map->is_sorted = true; } return bsearch(key, map->memb, map->memb_cnt, map->memb_sz, map->compar_fn); } // Example usage with user-defined type typedef struct my_val_st { uint16_t index; uint8_t subindex; uint32_t value; } my_val_t; int compar_uint32(uint32_t a, uint32_t b) { if (a < b) return -1; if (a > b) return 1; return 0; } int my_val_compar(const void *a, const void *b) { const my_val_t *mv_a = a; const my_val_t *mv_b = b; int cmp = compar_uint32(mv_a->index, mv_b->index); if (cmp) return cmp; return compar_uint32(mv_a->subindex, mv_b->subindex); } int main() { map_t *map = map_new(sizeof(my_val_t), my_val_compar); map_insert(map, &(my_val_t){ .index = 0x1001, .subindex = 0x0, .value = 99 }); map_insert(map, &(my_val_t){ .index = 0x1000, .subindex = 0x0, .value = 42 }); map_insert(map, &(my_val_t){ .index = 0x1000, .subindex = 0x1, .value = 17 }); const my_val_t *item; item = map_get(map, &(my_val_t){ .index = 0x1000, .subindex = 0x0 }); printf("value: %u\n", item->value); item = map_get(map, &(my_val_t){ .index = 0x1000, .subindex = 0x1 }); printf("value: %u\n", item->value); item = map_get(map, &(my_val_t){ .index = 0x1001, .subindex = 0x0 }); printf("value: %u\n", item->value); }

Dold text

Koden ovan hanterar inte att RAM tar slut, en övning till läsaren

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Skrivet av Ingetledigtnamn:

Att skippa storlekshanteringen förenklade koden, fint.

Om du vill göra koden ännu enklare kan du slå ihop index och sub_index till ett värde:

unit32_t key(uint16_t index, uint8_t sub_index) { return index << 16 | sub_index; }

Då kan du göra båda jämförelserna i en 32-bitsoperation.

Nästa steg är att inte köra en länkad lista. Den länkade listan är en ineffektiv datastruktur då du måste linjärsöka för att hitta dit data och du får en del overhead i samband med malloc. Stoppa dina DictionaryObject i en tabell (array) istället. Då blir du av med next-pekaren. Du kan antingen ha en "tillräckligt" stor array från början eller allokera den dynamiskt och göra en realloc för att göra den större när den blir full. När du har ditt data i en sorterad tabell, då kan du binärsöka. När du binärsöker i en tabell där varje element består av nyckel + värde så söker du bara på nyckeln, inte hela strukturen.

Så här!
Binärsökning då allt är linjärt sorterat.

https://onlinegdb.com/uwqRf1_7S

Permalänk
Skrivet av Yoshman:

Binärsökning blir lite poänglöst om man har en länkad lista, i det läget är det snabbare att linjärsöka.

C har ju inbyggt stöd för både sortering och binärsökning, detta är en möjlighet (förutsätter C99)

#include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // Generic C map with O(log(N)) search complexity typedef int (*map_compar_fn)(const void *, const void *); typedef struct map_st { bool is_sorted; map_compar_fn compar_fn; size_t memb_sz; size_t memb_cnt; void *memb; } map_t; map_t* map_new(size_t memb_sz, map_compar_fn compar_fn) { map_t *map = calloc(1, sizeof(map_t)); map->memb_sz = memb_sz; map->compar_fn = compar_fn; return map; } void map_insert(map_t *map, void *item) { map->memb_cnt += 1; map->memb = realloc(map->memb, map->memb_cnt * map->memb_sz); memcpy((char*)map->memb + (map->memb_cnt - 1) * map->memb_sz, item, map->memb_sz); map->is_sorted = false; } void* map_get(map_t *map, const void *key) { if (!map->is_sorted) { qsort(map->memb, map->memb_cnt, map->memb_sz, map->compar_fn); map->is_sorted = true; } return bsearch(key, map->memb, map->memb_cnt, map->memb_sz, map->compar_fn); } // Example usage with user-defined type typedef struct my_val_st { uint16_t index; uint8_t subindex; uint32_t value; } my_val_t; int compar_uint32(uint32_t a, uint32_t b) { if (a < b) return -1; if (a > b) return 1; return 0; } int my_val_compar(const void *a, const void *b) { const my_val_t *mv_a = a; const my_val_t *mv_b = b; int cmp = compar_uint32(mv_a->index, mv_b->index); if (cmp) return cmp; return compar_uint32(mv_a->subindex, mv_b->subindex); } int main() { map_t *map = map_new(sizeof(my_val_t), my_val_compar); map_insert(map, &(my_val_t){ .index = 0x1001, .subindex = 0x0, .value = 99 }); map_insert(map, &(my_val_t){ .index = 0x1000, .subindex = 0x0, .value = 42 }); map_insert(map, &(my_val_t){ .index = 0x1000, .subindex = 0x1, .value = 17 }); const my_val_t *item; item = map_get(map, &(my_val_t){ .index = 0x1000, .subindex = 0x0 }); printf("value: %u\n", item->value); item = map_get(map, &(my_val_t){ .index = 0x1000, .subindex = 0x1 }); printf("value: %u\n", item->value); item = map_get(map, &(my_val_t){ .index = 0x1001, .subindex = 0x0 }); printf("value: %u\n", item->value); }

Dold text

Koden ovan hanterar inte att RAM tar slut, en övning till läsaren

Jag lyckades med binärsökning utan minnesallokering

Se här:
https://onlinegdb.com/vH7MVBt_wL

Jag skulle kunna göra så att det blir dynamiskt när man ska addera ett index.

Permalänk

Du gillade inte funktionen bsearch i Cs standardbibliotek?

En sak som skulle göra koden enklare vore att låta en bit representera läs/skriv-access. Det är vanligt att man fuskar lite med enum typen:

typedef enum{ DO_ACCESS_READ = 0x1, DO_ACCESS_WRITE = 0x2, DO_ACCESS_READ_WRITE = DO_ACCESS_READ | DO_ACCESS_WRITE }DO_ACCESS;

då kan du göra en test om du har läs/skrivrätt:

if(dictionary_object[i].access & DO_ACCESS_READ)

En sak du bör tänka på är att binärsökning bara fungerar om tabellen är sorterad.

Permalänk
Skrivet av Ingetledigtnamn:

Du gillade inte funktionen bsearch i Cs standardbibliotek?

En sak som skulle göra koden enklare vore att låta en bit representera läs/skriv-access. Det är vanligt att man fuskar lite med enum typen:

typedef enum{ DO_ACCESS_READ = 0x1, DO_ACCESS_WRITE = 0x2, DO_ACCESS_READ_WRITE = DO_ACCESS_READ | DO_ACCESS_WRITE }DO_ACCESS;

då kan du göra en test om du har läs/skrivrätt:

if(dictionary_object[i].access & DO_ACCESS_READ)

En sak du bör tänka på är att binärsökning bara fungerar om tabellen är sorterad.

Jag har lyckats implementera binärsökning och det fungerar.
Nu så vill jag ha realloc när jag skapar nytt index. Men jag får det inte att fungera för att dictionary_object är null.

https://onlinegdb.com/QjqvGx4YB

Varför stannar programmet när jag anropar dictionary_object->key ?
Se denna rad 105.

printf("middle = %i, key = %i, dictionary_object->key = %i\n", middle, key, dictionary_object->key);

Tabellen kommer vara sorterad från början.

Edit:

Jag skulle tydligen använda detta

struct Dictionary_object* dictionary_object = (struct Dictionary_object*)calloc(0, sizeof(struct Dictionary_object));

istället för detta

struct Dictionary_object* dictionary_object = NULL;

Jag trodde realloc kunde ta NULL som första argument.

Permalänk

Du kanske måste returnera den nya pekaren du får från realloc. Det är inte säkert att realloc returnerar samma pekar som du skickar in.

Permalänk
Skrivet av Ingetledigtnamn:

Du kanske måste returnera den nya pekaren du får från realloc. Det är inte säkert att realloc returnerar samma pekar som du skickar in.

Jag löste detta med att använda calloc där jag deklarerade en noll-längd struktur.

Häftiga saker! Jag har programmerat mycket i C, men nu känner jag verkligen djupet av C Och detta är säkerligen inte ens början.

Permalänk

Nej, du löste det inte. Det bara råkar inte krascha.

Du får en ny pekare från realloc. Det är den du skall använda i framtiden. Den gamla kan realloc ha gjort free på. Du måste returnera den nya pekaren till caller.

Permalänk
Hedersmedlem
Skrivet av heretic16:

Jag trodde realloc kunde ta NULL som första argument.

Det kan den mycket riktigt, men det är som sagt den pekare som returneras som du vill spara.