[[:improve]] Viele Eigenschaften der Programmiersprache C basieren aufgrund der Nähe zu [[coding:assembly|Assembly]]. Siehe auch [[coding:cpp|C++]]/[[coding:csharp|C#]]. {{winapi_hwnd.pdf||Hwnd}} =====Datentypen===== ====Integer==== // testValue unsigned long long testValue = 0xFFFFFFFFFFFFFFFF; // 18446744073709551615 // 1 byte -> [0-255] or [0x00-0xFF] unsigned char numberChar = testValue; // 255 // 2 bytes -> [0-65535] or [0x0000-0xFFFF] unsigned short numberShort = testValue; // 65535 // 4 bytes -> [0-4294967295] or [0x00000000-0xFFFFFFFF] unsigned int numberInt = testValue; // 4294967295 // 4 bytes -> [0-4294967295] or [0x00000000-0xFFFFFFFF] unsigned long numberLong = testvalue; // 8 bytes -> [0-18446744073709551615] or [0x0000000000000000-0xFFFFFFFFFFFFFFFF] unsigned long long numberLongLong = testValue; // 18446744073709551615 printf("%u %d %u %lu %llu", numberChar, numberShort, numberInt, numberLong, numberLongLong); ====Float==== =====Strtok===== #include int main() { char buf[] = "Hello world, how are you?"; char *token = strtok(buf, " "); while(token != NULL) { printf("token: %s\n", token); token = strtok(NULL, " "); } } Nested strtok_r: https://stackoverflow.com/questions/4693884/nested-strtok-function-problem-in-c =====Statische Variablen===== // Eine statische Variable innerhalb einer Funktion behält ihren Wert auch zwischen mehreren Aufrufen. void foo() { int a = 10; static int sa = 10; a += 5; sa += 5; printf("a = %d, sa = %d\n", a, sa); } int main() { int i; for (i = 0; i < 10; ++i) foo(); } =====Pointer===== Siehe [[:c_pointer|Pointer in C]]. int array[5]; // Declares 5 contiguous integers int *ptr = array; // Arrays can be used as pointers ptr[0] = 1; // Pointers can be indexed with array syntax *(array + 1) = 2; // Arrays can be dereferenced with pointer syntax *(1 + array) = 3; // Pointer addition is commutative 2[array] = 4; // Subscript operator is commutative char a, b; // 2 chars = 2 bytes char *zeiger; // einen (char) zeiger zeiger = &a; // zeiger beinhaltet adresse von a *zeiger = 'A'; // schreibe ein 'A' an die im Zeiger gespeicherte Adresse von a printf("Kapitel %c\n", a); zeiger = &b; // zeiger beinhaltet adresse von b *zeiger = 'B'; // schreibe ein 'B' an die im Zeiger gespeicherte Adresse von b printf("Kapitel %c\n", b); // Pointer auf eine Funktion void func(int *i) { (*i)++; // *i++ geht nicht! Siehe Pointer Arithmetik } int main() { void (*func_ptr)(int*) = &func; int i = 0; (*func_ptr)(&i); printf("%d", i); } =====Pass by reference===== // pass a pointer void modify(char *s){ s[0] = 'g'; } int main(){ char name[] = "New Holland"; char *p_name = name; modify(p_name); printf("%s\n", name); } // pass the address of a pointer void modify(char **s){ char *new_name = "Old Holland"; *s = new_name; } int main(){ char name[] = "New Holland"; char *p_name = name; modify(&p_name); printf("%s\n", name); } =====Constants===== In compiled code there are several segments. * Code, Segment (for the actual rom code) * Data Segment (for initialised global and static variables) * Stack segment (local variables, return address, ..) * Heap segment (all dynamic allocations) * BSS (uninitialised global and static variables) Every segment has a read only region where constants are stored. The compiler - and only the compiler - has to decide in which segment a variable get stored. And then, if the variable is a const it is stored in the r/o region of that specific segment. * A local const int is stored in the r/o region of stack segment. * A global const int is stored in the r/o region of data segment. * Any uninitialised const int is stored in the r/o BSS segment. #include int main() { const int a = 10; int b = 20; const int *ptr = &a; printf("ptr: %d\n", *ptr); // error!!! *ptr = b; ptr = &b; printf("ptr: %d\n", *ptr); } See also [[coding:cpp#constants|constants in CPP]] =====Binary Operations===== void print(int n) { for (int i = 0; i < 8; i++) { printf("%d", !!((n << i) & 0x80)); } } int main() { unsigned int a = 44; /* 0010 1100 */ unsigned int b = 13; /* 0000 1101 */ print(a); printf(" => a\n"); print(b); printf(" => b\n--------\n"); int c = 0; c = a & b; print(c); printf(" => a & b\n"); c = a | b; print(c); printf(" => a | b\n"); c = a ^ b; print(c); printf(" => a ^ b\n"); c = ~a; print(c); printf(" => ~a\n"); c = a << 2; print(c); printf(" => a << 2\n"); c = a >> 2; print(c); printf(" => a >> 2"); } & (AND) 1 & 1 == 1 1 & 0 == 0 0 & 1 == 0 0 & 0 == 0 00110010 - b & 00010000 - & 0x10 ---------- 00010000 - result | (OR) 1 | 1 == 1 1 | 0 == 1 0 | 1 == 1 0 | 0 == 0 00110010 - b | 00000100 - | 0x04 ---------- 00110110 - result ^ (XOR) 1 ^ 1 == 0 1 ^ 0 == 1 0 ^ 1 == 1 0 ^ 0 == 0 00110010 - b ^ 00011000 - ^ 0x18 ---------- 00101010 - result 00101010 - b ^ 00011000 - ^ 0x18 ---------- 00110010 - result ~ (Inversion, One complement) 00000011 - 0x03 11111100 - ~0x03 b << >> (Bit shifting) 00001100 - b 00110000 - b << 2 00000011 - b >> 2 Siehe [[:logikgatter|Wahrheitstabelle]]. [[https://www.youtube.com/watch?v=F8kx56OZQhg|OneLoneCoder bitwise operations]] =====Memory===== Initialisieren int main() { char str[20]; // Setzen aller Elemente des Arrays auf 'A' memset(str, 'A', sizeof(str)); printf("Initialized string: %s\n", str); return 0; } Kopieren int main() { char source[] = "Hello, World!"; char destination[20]; // Kopieren des Inhalts von source nach destination memcpy(destination, source, sizeof(source)); printf("Copied string: %s\n", destination); return 0; } Vergleichen int main() { char str1[] = "abc"; char str2[] = "abd"; // Vergleichen der beiden Strings int result = memcmp(str1, str2, sizeof(str1)); if (result == 0) { printf("Strings are equal.\n"); } else if (result < 0) { printf("String 1 is less than String 2.\n"); } else { printf("String 1 is greater than String 2.\n"); } return 0; } =====Memory allocation===== ''malloc'' and ''calloc'' are almost the same except ''calloc'' zerores out the block. malloc takes only the size as arg, ''calloc'' need size and type. ''realloc'' is king. int main() { char *ptr = (char *)malloc(1024 * sizeof(char)); char buf1[1024] = {'a'}; strcat(ptr, buf1); ptr = realloc(ptr, 1024 * sizeof(char)); char buf2[1024] = {'B'}; strcat(ptr, buf2); printf("%s", ptr); free(ptr); } =====Buffer===== #include #include #define SIZE 64 unsigned char memory[SIZE]; void print_memory() { printf("\n\t00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15"); for(int i = 0; i < SIZE; i++) { if(i % 16 == 0) printf("\n%04d:\t", i); printf("%02x ", memory[i]); } printf("\n"); } int main() { int offset = 0; memset(memory, SIZE, 0); char str[] = "Hello world"; memcpy(memory + offset, &str, strlen(str)); offset += strlen(str); print_memory(); float f = 3.14159f; memcpy(memory + offset, &f, sizeof(float)); //offset += sizeof(float); print_memory(); float tmp; memcpy(&tmp, &memory[offset], sizeof(float)); printf("%f", tmp); return 1; } =====Files===== FILE *f; f = fopen("test.txt", "w"); char msg[] = "Hello world"; fwrite(msg, sizeof(msg), 1, f); fclose(f); f = fopen("test.txt", "r"); fseek(f, 0L, SEEK_END); int len = ftell(f); rewind(f); char c, buffer[len]; int i = 0; while( fread(&c, sizeof(char), 1, f) > 0 ) buffer[i++] = c; fclose(f); buffer[i] = '\0'; printf("%s", buffer); f = fopen("test.txt", "r"); char ch; while((ch = fgetc(f)) != EOF) printf("%c", ch); fclose(f); f = fopen("test.txt", "r"); char *line = NULL, read; size_t len = 0; while((read = getline(&line, &len, f)) > -1) printf("(%d) %s", read, line); std::ifstream f("test.txt"); std::string data, line; while(std::getline(f, line)) data += line; std::cout << data; Read 4 chars as a single int FILE *f = fopen("test.txt", "rw"); char msg[] = {0x41,0x42,0x43,0x44}; fwrite(&msg, 1, sizeof(char), f); int *buffer; fread(&buffer, sizeof(int), 1, f); fclose(f); printf("%d", buffer); // 0x41424344 = 1145258561 Piping in windows #include #include int main(void){ char psBuffer[128]; FILE *pPipe; if((pPipe = _popen("curl https://orf1.mdn.ors.at/out/u/orf1/q4a/manifest.m3u8", "rt")) == NULL ) exit(1); while(fgets(psBuffer, 128, pPipe)) { printf(psBuffer); } if (feof( pPipe)) printf("\nreturn %d\n", _pclose(pPipe)); else printf( "Failed\n"); } =====Data Structures===== ====Arrays==== #include int main() { // Erstellen eines Arrays mit 5 Elementen vom Typ int int array[5] = {1, 2, 3, 4, 5}; // Zugriff auf Elemente des Arrays printf("Element 0: %d\n", array[0]); printf("Element 3: %d\n", array[3]); return 0; } ====Struct==== struct Data { int i; float f; char *str; }; int main( ) { // like this struct Data data = {10, 2.5}; // or like this struct Data data; data.i = 10; data.f = 2.5; data.str = "Hello world"; printf( "data.i : %d\n", data.i); printf( "data.f : %f\n", data.f); printf( "data.str : %s\n", data.str); } Recursive Structure typedef struct Node { char *value; struct Node* left; struct Node* right; } Node; Structure in anderer Structure struct Details { char *name; }; struct Entry { int id; struct Details details; }; int main() { struct Entry entry = {1, "Manuel"}; printf("id: %d\n", entry.id); printf("name: %s\n", entry.details.name); } Structures mit void* Pointern typedef void (*Callback)(); struct Table { int id; char *type; void *val; Callback callback; }; void myFunction(const char *s) { printf("%s", s); } int main() { struct Table *t = malloc(sizeof(struct Table)); t->id = 1; t->type = "string"; char s[] = "Hello world, heheh"; t->val = &s; t->callback = myFunction; t->callback(t->val); } ====Linked List==== #include #include #include #include struct node { int data; int key; char *c; struct node *next; }; struct node *head = NULL; struct node *current = NULL; //display the list void printList() { struct node *ptr = head; printf("\n[ "); //start from the beginning while(ptr != NULL) { printf("(%d,%d) ",ptr->key,ptr->data); ptr = ptr->next; } printf(" ]"); } //insert link at the first location void insertFirst(int key, int data) { //create a link struct node *link = (struct node*) malloc(sizeof(struct node)); link->key = key; link->data = data; //point it to old first node link->next = head; //point first to new first node head = link; } //delete first item struct node* deleteFirst() { //save reference to first link struct node *tempLink = head; //mark next to first link as first head = head->next; //return the deleted link return tempLink; } //is list empty bool isEmpty() { return head == NULL; } int length() { int length = 0; struct node *current; for(current = head; current != NULL; current = current->next) { length++; } return length; } //find a link with given key struct node* find(int key) { //start from the first link struct node* current = head; //if list is empty if(head == NULL) { return NULL; } //navigate through list while(current->key != key) { //if it is last node if(current->next == NULL) { return NULL; } else { //go to next link current = current->next; } } //if data found, return the current Link return current; } //delete a link with given key struct node* delete(int key) { //start from the first link struct node* current = head; struct node* previous = NULL; //if list is empty if(head == NULL) { return NULL; } //navigate through list while(current->key != key) { //if it is last node if(current->next == NULL) { return NULL; } else { //store reference to current link previous = current; //move to next link current = current->next; } } //found a match, update the link if(current == head) { //change first to point to next link head = head->next; } else { //bypass the current link previous->next = current->next; } return current; } void sort() { int i, j, k, tempKey, tempData; struct node *current; struct node *next; int size = length(); k = size ; for ( i = 0 ; i < size - 1 ; i++, k-- ) { current = head; next = head->next; for ( j = 1 ; j < k ; j++ ) { if ( current->data > next->data ) { tempData = current->data; current->data = next->data; next->data = tempData; tempKey = current->key; current->key = next->key; next->key = tempKey; } current = current->next; next = next->next; } } } void reverse(struct node** head_ref) { struct node* prev = NULL; struct node* current = *head_ref; struct node* next; while (current != NULL) { next = current->next; current->next = prev; prev = current; current = next; } *head_ref = prev; } void main() { insertFirst(1,10); insertFirst(2,20); insertFirst(3,30); insertFirst(4,1); insertFirst(5,40); insertFirst(6,56); printf("Original List: "); //print list printList(); while(!isEmpty()) { struct node *temp = deleteFirst(); printf("\nDeleted value:"); printf("(%d,%d) ",temp->key,temp->data); } printf("\nList after deleting all items: "); printList(); insertFirst(1,10); insertFirst(2,20); insertFirst(3,30); insertFirst(4,1); insertFirst(5,40); insertFirst(6,56); printf("\nRestored List: "); printList(); printf("\n"); struct node *foundLink = find(4); if(foundLink != NULL) { printf("Element found: "); printf("(%d,%d) ",foundLink->key,foundLink->data); printf("\n"); } else { printf("Element not found."); } delete(4); printf("List after deleting an item: "); printList(); printf("\n"); foundLink = find(4); if(foundLink != NULL) { printf("Element found: "); printf("(%d,%d) ",foundLink->key,foundLink->data); printf("\n"); } else { printf("Element not found."); } printf("\n"); sort(); printf("List after sorting the data: "); printList(); reverse(&head); printf("\nList after reversing the data: "); printList(); } * Zentut Linked Lists http://www.zentut.com/c-tutorial/c-linked-list/ * https://www.tutorialspoint.com/data_structures_algorithms/linked_list_program_in_c.htm * https://stackoverflow.com/questions/50223927/c-how-to-read-in-a-linked-list-from-a-binary-file (BINARY) ====Hash Table==== #include #include #define SIZE 10 struct Node { int key; int value; struct Node* next; }; struct Node* hashTable[SIZE]; int hashFunction(int key) { return key % SIZE; } void insert(int key, int value) { struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); newNode->key = key; newNode->value = value; newNode->next = NULL; int index = hashFunction(key); if (hashTable[index] == NULL) { hashTable[index] = newNode; } else { struct Node* current = hashTable[index]; while (current->next != NULL) { current = current->next; } current->next = newNode; } } int search(int key) { int index = hashFunction(key); struct Node* current = hashTable[index]; while (current != NULL) { if (current->key == key) { return current->value; } current = current->next; } return -1; } int main() { // Initialisierung der Hash-Tabelle for (int i = 0; i < SIZE; i++) { hashTable[i] = NULL; } // Einfügen von Elementen insert(1, 10); insert(2, 20); insert(11, 30); // Suche nach einem Schlüssel int value = search(2); if (value != -1) { printf("Value found: %d\n", value); } else { printf("Value not found.\n"); } return 0; } ====Stack==== #include #include #define MAX_SIZE 100 struct Stack { int items[MAX_SIZE]; int top; }; void initialize(struct Stack* stack) { stack->top = -1; } int isEmpty(struct Stack* stack) { return stack->top == -1; } int isFull(struct Stack* stack) { return stack->top == MAX_SIZE - 1; } void push(struct Stack* stack, int item) { if (isFull(stack)) { printf("Stack overflow!\n"); return; } stack->items[++stack->top] = item; } int pop(struct Stack* stack) { if (isEmpty(stack)) { printf("Stack underflow!\n"); return -1; } return stack->items[stack->top--]; } int main() { struct Stack stack; initialize(&stack); push(&stack, 1); push(&stack, 2); push(&stack, 3); printf("Popped: %d\n", pop(&stack)); printf("Popped: %d\n", pop(&stack)); printf("Popped: %d\n", pop(&stack)); return 0; } ====Queue==== #include #include #define MAX_SIZE 100 struct Queue { int items[MAX_SIZE]; int front; int rear; }; void initialize(struct Queue* queue) { queue->front = -1; queue->rear = -1; } int isEmpty(struct Queue* queue) { return queue->front == -1; } int isFull(struct Queue* queue) { return (queue->rear + 1) % MAX_SIZE == queue->front; } void enqueue(struct Queue* queue, int item) { if (isFull(queue)) { printf("Queue overflow!\n"); return; } if (isEmpty(queue)) { queue->front = 0; queue->rear = 0; } else { queue->rear = (queue->rear + 1) % MAX_SIZE; } queue->items[queue->rear] = item; } int dequeue(struct Queue* queue) { if (isEmpty(queue)) { printf("Queue underflow!\n"); return -1; } int item = queue->items[queue->front]; if (queue->front == queue->rear) { queue->front = -1; queue->rear = -1; } else { queue->front = (queue->front + 1) % MAX_SIZE; } return item; } int main() { struct Queue queue; initialize(&queue); enqueue(&queue, 1); enqueue(&queue, 2); enqueue(&queue, 3); printf("Dequeued: %d\n", dequeue(&queue)); printf("Dequeued: %d\n", dequeue(&queue)); printf("Dequeued: %d\n", dequeue(&queue)); return 0; } =====Signed unsigned===== By default an (ANSI C) integer is signed. int a = 5; int b = -10; (a>b) ? puts("a>b") : puts("-"); unsigned int c = 5; int d = -10; (c>d) ? puts("c>d") : puts("-"); printf("%08x %08x %08x %08x", a,b,c,d); =====Makros===== * The ## operator takes two separate tokens and pastes them together to form a single token. * The # Operator will "stringify" the following token. * siehe http://www.eckart-winkler.de/computer/c/makros.htm #define f(a,b) a##b // strcat #define h(a) g(a) // exec #define g(a) #a // stringify int main() { printf("%s\n",h(f(1,2))); printf("%s\n",g(f(1,2))); return 0; } =====Preprocessing===== Gilt auch für [[coding:cpp|CPP]]. Compileranweisungen vor dem eigentlichen Kompilierung ausführen. Dabei werden z.B makros durch den eigentlichen Code ersetzt. Das folgende Programm mit ''gcc -E'' vorkompiliert #define DEFINE_LL_NODE(CONCRETE_TYPE) \ struct node_of_ ## CONCRETE_TYPE { \ CONCRETE_TYPE data; struct node_of_ ## CONCRETE_TYPE *next; \ }; \ #define DECLARE_LL_NODE(CONCRETE_TYPE, VARIABLE_NAME) \ struct node_of_ ## CONCRETE_TYPE VARIABLE_NAME; /* Declarations for types int and char */ DEFINE_LL_NODE(int) DEFINE_LL_NODE(char) int main (void) { /* Declaration of instances of each type. */ DECLARE_LL_NODE (int, foo) DECLARE_LL_NODE (char, bar) /* And you can then use these instances. */ foo.data = 1; foo.next = NULL; bar.data = 'c'; bar.next = NULL; } erzeugt ein Object file das so aussieht. # 1 "main.c" # 1 "" # 1 "" # 1 "main.c" # 10 "main.c" struct node_of_int { int data; struct node_of_int *next; }; struct node_of_char { char data; struct node_of_char *next; }; int main (void) { struct node_of_int foo; struct node_of_char bar; foo.data = 1; foo.next = NULL; bar.data = 'c'; bar.next = NULL; } =====Stdarg===== Pass multiple (unknown at runtime) parameters to a function. #include // define known parameters, everything after ",..." must be the same Datatype!!! void myfunc(const char *str, ...) { va_list list; va_start(list, str); while(str) { printf("%s", str); str = va_arg(list, const char *); } va_end(list); } int main() { char *a = "a"; char *b = "b"; myfunc(a,b,NULL); } =====Classes===== typedef struct Class Class; struct Class { double width, height; /* Variables */ double (*area)(Class *_class); /* Function pointer */ }; /* Function */ double calc(Class *_class) { return _class->width * _class->height; } /* Constructor */ Class _Class() { Class s; s.width = 1; s.height = 1; s.area = calc; return s; } int main() { Class s1 = _Class(); s1.width = 4; s1.height = 5; printf("width = %f\n", s1.width); printf("height = %f\n", s1.height); printf("area = %f\n", s1.area(&s1)); }; #include typedef struct { void (*someMethod)(void); } Test; void hello(void) { printf("hello"); } void world(void) { printf("world"); } int main(void) { Test test; test.someMethod = "hello"; test.someMethod(); test.someMethod = "world"; test.someMethod(); } See also [[coding:cpp#classes|classes in CPP]] =====Inline Assembly===== GCC #include int main() { int a = 5, b = 10, result; asm volatile ( "movl %1, %%eax;" // Move a into EAX register "movl %2, %%ebx;" // Move b into EBX register "addl %%ebx, %%eax;" // Add EBX to EAX "movl %%eax, %0;" // Move the result back to variable result : "=r" (result) // Output operand : "r" (a), "r" (b) // Input operands : "%eax", "%ebx" // Clobbered registers ); printf("Result: %d\n", result); return 0; } Visual C++ #include int main() { int a = 5, b = 10, result; __asm { mov eax, a // Move a into EAX register mov ebx, b // Move b into EBX register add eax, ebx // Add EBX to EAX mov result, eax // Move the result back to variable result } printf("Result: %d\n", result); return 0; } [[https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html|Extended inline ASM]] =====Resources===== Datei resource.rc erstellen #ifndef RESOURCE_RC_INCLUDED #define RESOURCE_RC_INCLUDED MAINICON ICON "icon.ico" #endif und windres resource.rc resource.o aufrufen. Optional, in C/CPP nochmal definieren #define MAINICON 101 Danach kann man das Programm wie üblich kompilieren. gcc prog.c resource.o -o prog.exe =====Processes===== #include int main(int argc, char *argv[]) { FILE *fp = popen("ipconfig", "w"); int i = 0; char line[1024]; while (fgets(line, sizeof(line), fp) != NULL) { printf("%s\n", line); } pclose(fp); return 0; } =====Signal===== #include void catch_function(int sig) { exit(EXIT_FAILURE); } signal(SIGINT, catch_function); =====declspec===== extern "C" { __declspec(dllexport) int add(int a,int b) { return a+b; } __declspec(dllexport) int subtract(int a,int b) { return a-b; } } =====Libraries und Shared objects===== // windows __declspec(dllexport) char *myfunc() { return "myfunc called"; } // linux __attribute__ ((visibility ("default"))) char *myfunc() { return "myfunc called"; } Diese Library Datei kompiliert man mit dem -c Parameter. // -fPIC für positionsunabhängigen Code // Windows dynamic gcc -c test.c -o test.o gcc -shared test.o -o test.dll gcc main.c -o main -L. -ljail // Windows static gcc -c -static test.c ar rcs test.a test.o gcc main.c -o main test.a // Linux dynamic gcc -c test.c gcc -shared test.o -o libTest.so // with lib prefix gcc main.c -o main -L. -lTest // without lib prefix // Linux static gcc -c test.c -o test.o ar crf libTest.a test.o // with lib prefix gcc main.c -o main -L. -lTest // without lib prefix #include #include #ifdef _WIN32 #include #else #include #endif #ifdef _WIN32 typedef HANDLE lib_t; #else typedef void* lib_t; #endif lib_t MyLoadLib(const char* lib) { #ifdef _WIN32 return LoadLibraryA(lib); #else return dlopen(lib, RTLD_LAZY); #endif } void MyUnloadLib(lib_t lib) { #ifdef _WIN32 FreeLibrary(lib); #else dlclose(lib); #endif } void* MyLoadProc(lib_t lib, const char* proc) { #ifdef _WIN32 return GetProcAddress(lib, proc); #else return dlsym(lib, proc); #endif } int main(int argc, char **argv) { lib_t myLib = NULL; myLib = MyLoadLib("./myLib.so"); void *funcname; funcname = "init"; void *(*func_ptr)() = MyLoadProc(myLib, funcname); char *result; result = (*func_ptr)(); printf("%s", result); MyUnloadLib(myLib); return 0; } =====Links===== * http://www.gowrikumar.com/c/index.php * https://de.wikibooks.org/wiki/C-Programmierung:_Komplexe_Datentypen * http://manpages.ubuntu.com/manpages/bionic/man1/elfrc.1.html * [[https://www.quora.com/How-is-a-hash-table-different-to-a-linked-list-or-an-array|Linked List, Hashtable, Array Comparision]] =====More===== * Managed Assembly https://stackoverflow.com/questions/973939