Search
Struktura je heterogenní datový typ, který slouží zejména ke spojení vzájemně souvisejících údajů (datových položek). Síla tohoto datového typu se obvykle projevuje v přehlednosti zdrojového kódu, ale lze ji i jednoduše využít pro návratové proměnné z funkcí.
struct jménoStruktury { struct student { typ jménoPoložky; char jmeno[20]; typ jménoPoložky; char prijmeni[20]; ... float prumer; } proměnné; } Marketa, Pavel;
Zavedení nového uživatelského datového typu je možné příkazem typedef. Syntaxe příkazu je:
typedef
typedef definice_typu identifikator_typu;
Nový datový typ můžeme vytvořit i pro stuktury (viz níže), což vede k pohodlnějšímu používání struktur.
K položkám struktury se přistupuje operátorem . (tečka). V případě ukazatele na strukturu pak operátorem → (šipka) - nahrazuje dereferenci s tečkovým operátorem (viz příklad). Všimněte si, že funkce vytvorStudenta vrací ukazatel na strukturu a funkce vypisStudenta přijímá ukazatel na strukturu, tj. není vytvářena lokální kopie pro dané funkce a program je efektivnější.
.
→
typedef struct { char name[20]; /* jmeno studenta */ float gpa; /* studijni prumer - grade point average */ } Student; Student* create_student() { Student *student = (Student*) malloc(sizeof(*student)); /* v tomto jednoduchem pripade predpokladem uspesnou alokaci pameti */ printf("Zadejte jmeno: "); scanf("%s", student->name); /* jmeno je typu pole znaku, ktere je reprezentovane adresou prvniho znaku */ printf("Zadejte studijni prumer: "); scanf("%f", &student->gpa); /* scanf prijima v parametru adresu, proto referencni operator & */ return student; } void print_student(Student *student) { printf("Vypis: %s, %.2f \n", student->name, student->gpa); /* stejne jako (*student).name a (*student).gpa */ } int main(int argc, char* argv[]) { /* inicializace struktury pri deklaraci */ Student student = {"Chuck Norris", 2}; student.gpa = 0.5; /* preci jenom se jedna o Chucka, proto upravuje jeho studijni prumer */ print_student (&student); print_student(create_student()); return 1; }
Syntaxe typu union je stejná jako u struktury, místo struct používáme klíčkové slovo union. Hlavní rozdíl je v tom, že struktura alokuje paměť pro všechny položky, typ union zabírá v paměti pouze tolik místa, kolik je potřeba pro uložení největší položky. Z toho vyplývá, že lze použít v jednom okamžiku pouze jednu datovou položku unionu.
typedef union { int temperature; /* teplota ohrivace */ char cooling; /* stav chlazeni */ } Heater; typedef struct { float temperature; Heater state; /* stav je union ve struktuře */ } Wheater; /* pocasi */
Teplota ohřívače je počítána jen když je teplota nízká, naopak stav chlazení je uváděn jen když je teplota vysoká; pokud je uvedena teplota, položka stav bude mít jen jednu hodnotu.
Výčtový typ enum, jehož podstata je stejná jako v jazyce Java, je v jazyce C chápán jako náhrada za konstanty vytvářené pomocí makra preprocesoru #define. Výčtovým typem je definována množina všech přípustných hodnot proměnné; vnitřně jsou pak tyto konstanty reprezentovány celými čísly zpravidla typu int, případně nejmenšího znaménkového typu, který je schopen rozsah množiny obsáhnout.
int
typedef enum { RED, GREEN, BLUE } Color;
Podobně jako v jsme si ukazovali v Javě, tak i v C můžeme vytvořit spojový seznam. Místo referenční proměnné využijeme však v C ukazatel na další prvek jako proměnnou typu ukazatel na strukturu.
typedef struct _student { /* musime deklarovat typ, abychom se mohli na nej odkaze v deklaraci polozky next */ char name[30]; float gpa; struct _student *next; /* Ukazatel na sebe sama, Student jeste neni znam proto pouzivame _student */ } Student; /* Student je jmeno zavedeneho typu struktury */ Student *create_student(Student *next) { Student *tmp = (Student*) malloc(sizeof (Student)); printf("Zadejte jmeno: \n"); scanf("%s", tmp->name); printf("Zadejte studijni prumer: \n"); scanf("%f", &tmp->gpa); tmp->next = next; return tmp; } void print_student(Student *student) { printf("%s, %.2f \n", student->name, student->gpa); } int main(int argc, char* argv[]) { Student *start = NULL, *cur; int i; for (i = 0; i < 3; i++) /* vytvoreni tri studentu */ start = create_student(start); cur = start; while (cur) { /* vypis vytvorenych studentu */ print_student(cur); cur = cur->next; } return (EXIT_SUCCESS); }
Použijte vhodné dělení do funkcí. Pro řazení si můžete naprogramovat svoji funkci, nebo lépe použijte funkci qsort z knihovny stdlib.
void qsort( void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
qsort
Pro porovnání textových řetězců si napište vlastní funkci nebo využijete funkci strcmp z knihovny string.h.
strcmp
string.h
int strcmp(const char *s1, const char *s2);
Pro jednoduchost předpokládejte, že všechny řetězce vždy končí znakem '\0' a jsou správně alokovány.
#define FNAME "osoby.txt" int write_person(Person *person, int number) { FILE *f; /* otevreni binarni souboru pro zapis */ if ((f = fopen(FNAME, "wb")) == NULL) { printf("Chyba zapisu do souboru %s.\n", FNAME); return 1; } if (number != fwrite(person, sizeof(Person), number, f)) { fclose(soubor); return 1; } fclose(f); return 0; } int read_person(Person *person) { FILE *f; Person tmp; int index = 0; /* otevreni binarniho souboru pro cteni */ if ((f = fopen(FNAME, "rb")) == NULL) { printf("Chyba cteni souboru %s.\n", FNAME); return 1; } while (fread(&tmp, sizeof(Person), 1, f) == 1) { person[index++] = tmp; /* vyuzivame operatoru prirazeni nad strukturou */ } fclose(f); return 0; }
Uvedený příklad neřeší alokaci struktury a bude správně fungovat pouze pro jedinou osobu nebo dle alokované paměti odkazované předávanou proměnnou person.
person
Vytvořte spojový seznam uchovávající seznam obdélníků s vlastnostmi šířka, výška a textovým popisem obsahujícím barvu. Seznam si bude uchovávat ukazatel na začátek i ukazatel na konec. Napište funkce pro
Implementujte předchozí úlohy s použitím seřazeného zřetězeného seznamu, od nejmenšího (plošně) po největší obdélník. Tedy například funkce pro vkládání prvku x bude vkládat na příslušnou pozici.
Pro seznamy z předchozích kapitol implementujte funkci, která spojí dva seznamy, resp. spojí dva seřazené seznamy v jeden nový, seřazený.
Binární vyhledávací strom (BVT) je datová struktura založená na binárním stromu, v němž jsou jednotlivé prvky uspořádány tak, aby v tomto stromu bylo možné rychle vyhledávat danou hodnotu. To zajišťují tyto vlastnosti:
Příklad BVT ukazuje následující obrázek:
Napište a vyzkoušejte funkce pro práci s BVT:
Součástí Java platformy je rozhraní JNI (Java Native Interface), které umožňuje propojit kód běžící na virtuálním stroji Javy s nativními programy a knihovnami napsanými v jiných programovacích jazycích (např. C). Hlavními výhodami jsou zvýšení výkonu v některých low-level úlohách (např. Math.sqrt(double a)) a umožnění komunikace s hardwarovými zařízeními (Bluetooth, apod.). Nevýhodou je především ztráta platformní nezávislosti, protože takový program využívá knihovny zkompilované pro daný operační systém.
Implementace nativní knihovny a propojení s Javou je znázorněno na obrázku výše. Příklad ilustruje vytvoření C knihovny, která vypisuje text “Hello World!” a její volání z Java aplikace. Celý proces implementace je možné shrnout do několika kroků:
class HelloWorld { static { /* statický inicializátor */ System.loadLibrary("HelloWorld"); /* načtení dynamické knihovny vytvořené v jazyku C */ } private native void print(); /* deklarace nativní metody */ public static void main(String[] args) { new HelloWorld().print(); } }
javah -jni HelloWorld
JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject);
-Wl,--add-stdcall-alias -mno-cygwin -shared -m32 -Wall -pedantic
#include <jni.h> #include <stdio.h> #include "HelloWorld.h" JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject obj) { printf("Hello World!\n"); return; }