Autorem tego opracowania jest dr Piotr A. Dybczyński z Instytutu Obserwatorium Astronomiczne UAM w Poznaniu.
Definicja struktury
Struktura jest obiektem stanowiącym nazwaną grupę zmiennych o różnych typach. Struktury wprowadza się zwykle dla zwiększenia przejrzystości programu lub dla ułatwienia przekazywania większych ilości powiązanych ze sobą danych. Poniżej mamy przykład definicji struktury, zawierającej komplet elementów orbity a niżej deklarację dwóch zmiennych strukturalnych. W tym szczególnym przypadku wszystkie elementy są typu double.
double q, /* perihelion distance */
e, /* eccentricity */
peri, /* argument of perihelion */
node, /* longitude of ascending node */
i, /* inclination of the orbit */
T; /* moment of perihelion passage */
};
struct orbita o1,o2;
// powyższe można zapisać krócej:
struct orbita{
double q, /* perihelion distance */
e, /* eccentricity */
peri, /* argument of perihelion */
node, /* longitude of ascending node */
i, /* inclination of the orbit */
T; /* moment of perihelion passage */
} o1,o2;
// można też skorzystać z możliwości definiowania własnego typu:
typedef struct{
double q, /* perihelion distance */
e, /* eccentricity */
peri, /* argument of perihelion */
node, /* longitude of ascending node */
i, /* inclination of the orbit */
T; /* moment of perihelion passage */
}orbita;
orbita o1,o2;
Do elementów struktury odwołujemy się w programie jak do zmiennych korzystając z notacji o1.peri, o2.e itp. W takim zapisie przed kropką (operatorem selekcji) występuje nazwa struktury (jednoznacznie identyfikująca miejsce jej przechowywania w pamięci komputera) a po kropce nazwa elementu (komponentu, składnika) struktury. Kolejność elementów w strukturze wpływa jedynie na kolejność ich umieszczenia w pamięci.
Wskaźniki do struktur i ich elementów
W wielu przypadkach potrzebne są wskaźniki (pointery) do struktur. Definiujemy je podobnie jak wskaźniki do zmiennych:
double q, /* perihelion distance */
e, /* eccentricity */
peri, /* argument of perihelion */
node, /* longitude of ascending node */
i, /* inclination of the orbit */
T; /* moment of perihelion passage */
};
struct orbita *p1;
double *ww;
struct orbita o1,o2;
/* Po tych deklaracjach w programie może pojawić się np. podstawienie: */
p1=&o2;
/* Jeśli potrzebujemy wskaźnika do poszczególnego elementu struktury to wykorzystuje się specjalną notację: */
ww = p1->node;
/* Przykłady z kodu wyliczającego elementy orbity ... */
elem->e = f/mi;
elem->p = c*c/mi;
elem->a = elem->p/(1.-elem->e*elem->e);
elem->q = elem->a*(1-elem->e);
elem->n = sqrt(mi/(elem->a*elem->a*elem->a));
Przykład zastosowania struktur - czytanie plików binarnych
Efemerydy planetarne JPL są w tej chwili podstawowym narzędziem do wyliczania położeń i prędkości planet, Księżyca i Słońca w układzie równikowym. Są to duże pliki binarne opublikowane wraz dokumentacją i oprogramowaniem w FORTRAN-ie do ich wykorzystania. Z konieczności wykonałem więc kiedyś pełne tłumaczenie tego oprogramowania na język C, dostępne jako public domain na naszym obserwatoryjnym serwerze ftp.
Zajmijmy się przykładowo dostępem do efemerydy DE221. Plik efemerydy to plik binarny z rekordami wielkości 8144 bajtów (każdy rekord to 1018 współczynników Czebyszewa po osiem bajtów - typ double).
Pierwsze dwa rekordy nie zawierają jednak tych współczynników tylko różne stałe tekstowe i numeryczne używane w efemerydzie.
Początek pierwszego rekordu w binarnym pliku efemerydy wygląda tak (dodałem przejście do nowego wiersza po każdych 84 bajtach dla zwiększenia czytelności):
JPL Planetary Ephemeris DE421/LE421 Start Epoch: JED= 2414864.5 1899-JUL-29-00:00:00 Final Epoch: JED= 2471184.5 2053-OCT-09-00:00:00 DENUM LENUM TDATEFTDATEBCENTERCLIGHTAU EMRAT GM1 GM2 GMB GM4 GM5 GM6 GM7 GM8 GM9 GMS RAD1 RAD2 RAD4 JDEPOCX1 Y1 Z1 XD1 YD1 ZD1 X2 Y2 Z2 XD2 YD2 ZD2 XB YB ZB XDB YDB ZDB X4 Y4 Z4 XD4 YD4 ZD4 X5 Y5 Z5 XD5 YD5 ZD5 X6 Y6 Z6 XD6 YD6 ZD6 X7 Y7 Z7 XD7 YD7 ZD7 X8 Y8 Z8 XD8 YD8 ZD8 X9 Y9 Z9 XD9 YD9 ZD9 XM YM ZM XDM YDM ZDM XC YC ZC XDC YDC ZDC BETA GAMMA J2SUN GDOT MA0001MA0002MA0004MAD1 MAD2 MAD3 RE ASUN PHI THT PSI OMEGAXOMEGAYOMEGAZS31M TAUE1 TAUE2 ROTEX ROTEY DROTEX COBLATEDOT IFAC KVC PHIC THTC PSIC OMGCX OMGCY OMGCZ AM J2M J3M J4M C22M C31M C32M C33M S32M S33M C41M S41M C42M S42M C43M S43M C44M S44M LBET LGAM K2M TAUM AE J2E J3E J4E K2E0 K2E1 K2E2 TAUE0 DROTEYGMAST1 GMAST2GMAST3PSIDOTMGMIS MA0007MA0324MA0003MA0006MA0009MA0010MA0019MA0020MA0024MA0031 MA0041MA0052MA0139MA0354MA0511MA0532MA0654MA0005MA0008MA0013MA0014MA0015MA0016MA0018 MA0022MA0023MA0027MA0029MA0045MA0051MA0065MA0078MA0097MA0105MA0111MA0344MA0372MA0405 MA0409MA0451MA0704MA0747MA0011MA0021MA0025MA0028MA0030MA0042MA0060MA0063MA0069MA0094 MA0098MA0135MA0145MA0187MA0192MA0194MA0216MA0230MA0337MA0419MA0488MA0554XS YS ZS XDS YDS ZDS ............................................................ ....................................................................................
Na podstawie dokumentacji do przeczytania tych dwóch pierwszych rekordów przygotowałem następujący kod:
#define RECSIZE 8144
FILE *F1;
struct rec1{
char ttl[3][84]; /* trzy wiersze opisu efemerydy */
char cnam[400][6]; /* nazwy stałych (400 sztuk po 6 znaków) */
double ss[3];
long int ncon; /* liczba wykorzystywanych stałych */
double au;
double emrat;
long int ipt[12][3];
long int numde;
long int lpt[3];
};
struct{
struct rec1 r1;
char spare[RECSIZE-sizeof(struct rec1)];
} R1;
struct rec2{
double cval[400];
};
struct{
struct rec2 r2;
char spare[RECSIZE-sizeof(struct rec2)];
} R2;
/* Dzięki powyższym deklaracjom przeczytanie i zinterpretowanie zawartości
pierwszych dwóch rekordów efemerydy załatwia instrukcja: */
fread(&R1,sizeof(R1),1,F1);
fread(&R2,sizeof(R2),1,F1);