poprzedni temat | w górę | następny temat

Złożone typy danych

Typ wyliczeniowy

Posługiwanie się wprost literałami liczbowymi jako identyfikatorami nie jest najlepszym pomysłem. Zamiast tego warto używać typu wyliczeniowego:

enum Kolor {
  CZARNY,
  BIALY,
  CZERWONY,
  ZIELONY,
  NIEBIESKI
};

enum Kolor c = BIALY;
if( c == BIALY ){
  c = CZERWONY;
}

Zwyczajowo wartości typu wyliczeniowego piszemy wielkimi literami, aby zaznaczyć, że są one stałe.

Struktury

Struktury (struct) umożliwiają definiowanie typów, których zmienne mogą zawierać w sobie składowe różnych typów.

struct Punkt {
  double x, y;
};

struct Adres {
  char miejscowosc[100];
  char ulica[100];
  int nr_domu;
  int kod;
};

Warto zwrócić uwagę, że po nawiasie klamrowym zamykającym musi się pojawić średnik.

Gdy już mamy zdefiniowane typy strukturalne jak powyżej, zadeklarujmy zmienne tych typów.

struct Punkt p1;
p1.x = 1.0;
p1.y = 2.25;

struct Adres adres;
strncpy(adres.miejscowosc, "Lublin", sizeof(adres.miejscowosc));
strncpy(adres.ulica, "Akademicka", sizeof(adres.ulica));
adres.nr_domu = 9;
adres.kod = 20033;

Wielkość zmiennej typu strukturalnego jest nie mniejsza od sumy rozmiarów składowych. Dlaczego nie zawsze równa? Zmienne typów większych od 1 B najczęściej nie są umieszczane w pierwszym wolnym miejscu dostępnej pamięci operacyjnej, lecz pod adresem równym wielokrotności rozmiaru zmiennej (nie dotyczy to tablic). Ta sama zasada odnosi się do składowych w strukturach. Więcej o tym można znaleźć w poradniku The Lost Art of C Structure Packing.

Operator ->

Mając wskaźnik p do struktury, możemy się dostać do jej składowej przy użyciu wyrażenia (*p).skladowa. Istnieje krótszy sposób, wykorzystujący operator ->: p->skladowa.

struct Adres *tablica_wskaznikow_na_adres[10];
int i;
for(i=0; i<10; ++i){
  tablica_wskaznikow_na_adres[i] = malloc(sizeof Adres);
  tablica_wskaznikow_na_adres[i]->kod = 20033;
  // niżej to samo inaczej:
  (*tablica_wskaznikow_na_adres[i]).kod = 20033;
}

Unie

Unie (union) tym różnią się od struktur, że ich rozmiar jest równy rozmiarowi największej składowej. Dzieje się to jednak kosztem tego, że w danym momencie pamiętana jest tylko jedna składowa (ta, która była ostatnio zapisana).

union Unia {
  int a;
  double b;
  char c;
};

union Unia u;
u.a = 2012;
u.c = 0xff; // == 255
printf("%d\n", u.a);

typedef

Słowo kluczowe typedef umożliwia stworzenie aliasu dla dowolnego typu. Załóżmy, że deklarujemy zmienną pewnego typu; jeśli dodamy na początku deklaracji słowo typedef, nazwa dotychczasowej zmiennej będzie oznaczała nazwę typu.

typedef int Dlugosc;
typedef int *Pint;
typedef Pint Int_tab3[3];

Dlugosc a = 12;
Int_tab3 tab = {&a, &a, &a};

W powyższym przykładzie Dlugosc to alias dla typu int, natomiast Int_tab3 to alias dla typu tablic trzyelementowych o elementach typu Pint czyli wskaźnikach na int.

Zadania

  1. Zdefiniuj typ strukturalny do przechowywania informacji o wektorach na płaszczyźnie. Napisz funkcję, która przyjmuje jako parametr taki wektor i zwraca jego długość.

  2. Napisz funkcję, która przyjmuje wskaźnik do wektora jak w poprzednim zadaniu i zmienia jego zwrot.

  3. Zdefiniuj typ strukturalny Punkt3D do przechowywania informacji o punktach w przestrzeni trójwymiarowej. Napisz funkcję, która przyjmuje dwa parametry takiego typu i zwraca odległość między oboma punktami.

  4. Zdefiniuj strukturę Zespolona służącą do przechowywania liczb zespolonych. Zdefiniowana struktura powinna zawierać pola im oraz re typu double służące do przechowywania części liczby zespolonej odpowiednio urojonej i rzeczywistej.

    Napisz funkcję zesp_suma, która dostaje dwa parametry typu Zespolona i zwraca ich sumę. Analogicznie zdefiniuj funkcję zesp_iloczyn.

  5. Zdefiniuj strukturę Osoba, która wśród swoich składowych pól będzie zawierała napis do przechowywania imienia oraz zmienną całkowitą do przechowywania roku urodzenia.

    Napisz funkcję, która przyjmuje tablicę struktur Osoba oraz jej rozmiar i wypisuje imię najstarszej osoby.

poprzedni temat | w górę | następny temat