Alaptípusok:
void | no type |
bit | single bit |
byte | signed 8 bits |
ubyte | unsigned 8 bits |
short | signed 16 bits |
ushort | unsigned 16 bits |
int | signed 32 bits |
uint | unsigned 32 bits |
long | signed 64 bits |
ulong | unsigned 64 bits |
cent | signed 128 bits (reserved for future use) |
ucent | unsigned 128 bits (reserved for future use) |
float | 32 bit floating point |
double | 64 bit floating point |
extended | largest hardware implemented floating point size (Implementation Note: 80 bits for Intel CPU's) |
imaginary | an extended floating point value, but with imaginary type |
complex | two extended floating point values, one real and the other imaginary |
char | unsigned 8 bit ASCII |
wchar | unsigned Wide char (Implementation Note: 16 bits on Win32 systems, 32 bits on linux, corresponding to C's wchar_t type) |
A bit adattípus speciális, egyetlen bináris bitet jelent. Pointerek vagy referenciák bitre nem megengedettek.
A deklarációs szintaxisokat általában balról jobbra olvassuk:
Ha a tömbök egymás mellett vannak, akkor jobbról balra olvassuk:
Függvényekre mutató pointereket úgy kell deklarálni, mint az aldeklarációkat:
C stílusú tömb-deklarációk, ahol a [] az azonosító jobb oldalán jelenik meg:
Többszörös deklaráció esetén az összes deklarációnak típusa meg kell egyezzen:
Új típust a 'typedef' kulcsszóval vezethetünk be. Az új, erős típus szemantikusan érthető lesz a típusellenőrző rendszer, a függvény felüldefiniálás és a debugger számára.
A típusdefinícióval kezdeti értéket is specifikálhatunk, amely eltér az alaptípus kezdeti értékétől:
Néha alkalmas lehet alias nevet használni egy típusra. A D-ben ezt alias deklarációval tehetjük meg:
Az alias típusok szemantikusan azonosak azzal a típussal , amelyből képezték. A debbuger nem tudja megkülönböztetni ezeket, és nincs különbség itt a függvény-felüldefiniálás esetén sem. Például:
Az alias típusok ekvivalensek a C typedef-el.
Egy szimbólumot deklarálhatunk egy másik szimbólum alias neveként. Például:
A következő alias deklarációk érvényesek:
Az alias szimbólumokat gyakran használjuk olyankor, amikor egy hosszú szimbólumnevet akarunk lerövidíteni, vagy amikor az egyik szimbólum referenciáját akarjuk átirányítani egy másik szimbólumra.
A D nyelvben négyféle tömb típus van:
int* p; Adatra mutató mutató | |
int[3] s; | Statikus tömb |
int[] a; | Dinamikus tömb |
int[char[]] x; | Asszociatív tömb |
Ezek egyszerű mutatók adatra, hasonlóan a C mutatókhoz. A mutatók arra szolgálnak, hogy C és specializált rendszerekkel kapcsolatot lehessen teremteni. Ezekhez nincs hossz információ társítva, és így nincs is lehetősége a fordítóprogram vagy a futtatási környezet számára, hogy indexelési határt vagy más memóriacímzési hibákat ellenőrizzen. A leggyakoribb használati esetekben ezeket a mutatókat helyettesíthetjük dinamikus tömbökkel, out és inout paraméterekkel és referencia típusokkal.
Ezek a C tömbökkel analóg módon használhatók. A statikus tömböket jellemzője, hogy fordítási időben ismert a méretük és nem is változtatható meg a továbbiakban.
Egy statikus tömb teljes mérete nem haladhatja meg a 16 Mb méretet. Amennyiben ennél nagyobb méretű tömbre van szükségünk, használjunk dinamikus tömböt.
A dinamikus tömbök egy hossz értékből és egy adathalmaz mutatójából áll. Több dinamikus tömb is osztozhat az adathalmaz egy részén vagy egészén.
Két módja van a tömbök deklarálásának: prefix és postfix. A prefix forma a javasolt, főleg abban az esetben, ha nem triviális típust deklarálunk.
A prefix deklarációk a deklarált azonosító előtt jelennek meg és jobbról balra olvashatók, azaz:
A postfix deklarációk a deklarált azonosító után jelennek meg és balról jobbra olvassuk őket. Mindegyik csoport ekvivalens deklarációkat sorol fel:
Indoklás: A postfix forma megegyezik azzal a móddal, ahogy tömböket C és C++ nyelveken deklaráljuk, ezáltal könnyebb tapasztalt C/C++ programozók számára a D nyelvre áttérés.
Két csoportba lehet sorolni a tömbökön végzett műveleteket - a tömb címén keresztül operáló műveletek, és a tömbök tartalmán keresztül operáló műveletek. A C csak a tömb címén keresztül tud operálni. A D nyelvben mindkettő mód használható.
A tömb címét a tömb neve határozza meg, mint a p, s vagy a:
Egy tömb szeletelése azt jelenti, hogy egy altömbjét határozzuk meg. Egy tömbszelet esetén az adat nem másolódik le, csak egy másik referencia jön létre rá. Például:
A [] egy rövidítése a teljes tömb szeletként kezelésére. Például, b tömb következő értékadásai szemantikailag mind megegyeznek:
A szeletelés nem csak azért hasznos, mert egy tömb részére hivatkozhatunk másik tömbként, hanem segítségével mutatókat indexhatár ellenőrzött tömbökké konvertálhatunk:
Bit tömbök szeletelése csak abban az esetben megengedett, ha a tömb alsó határa byte határra esik:
Bit tömbök esetén a rossz szelet hivatkozási kísérlet futási időben ArrayBoundsError kivételt vált ki.
Amikor egy szelet operátor egy értékadás balértékeként jelenik meg egy kifejezésben, azt jelenti, hogy a tömb tartalma a célterület, nem pedig a tömb referenciája. A tömb másolás akkor történik, amikor a balérték egy szelet, és a jobbérték egy tömb vagy egy mutató amely ugyanolyan típusra mutat.
Az egymást átfedő területek másolása hiba:
Azáltal, hogy megtiltjuk az egymást átfedő területek másolását lehetővé válik sokkal agresszívebb párhuzamos kód optimalizáció, mint amit a C soros szemantikája lehetővé tesz.
Ha egy szelet operátor szerepel egy értékadás bal értékeként, és a jobbérték típusa megegyezik a balérték elemtípusával, akkor a bal oldal minden elemének értékül lesz adva a jobboldal.
A ~ bináris operátor az összefűző operátor. Arra használt, hogy tömböket fűzzünk össze:
Sok nyelv a + operátort terheli túl, hogy összefűzésre használja. Ez félreértésekhez vezethet:
Az előző kifejezés 13 értékű számot vagy a vagy a "103" értékű karakterláncot adja eredményként? nem egyértelmű, és a nyelv tervezők körültekintően szabályokat határoznak meg, hogy hogyan kell értelmezni - szabályok, amelyek hibásan kerülnek implementálásra, elkrülik a figyelmünket, elfelejtődnek és figyelmen kívül hagyásra kerülnek. Sokkal jobb, hogy van egy + operátor, amely az összeadást jelenti, és egy külön operátor a tömbök összefűzésére.
Hasonlóan, a ~= operátor összefűzést jelent:
Az összefűzés mindig létrehoz egy másolatot az operandusaiból, akkor is ha az egyik operandus egy 0 hosszúságú tömb, azaz:
Általánosságban, az (a[n..m] op e) úgy definiált, mint:
Azaz a következő kifejezés:
eredmény szempontjából ekivalens a következő kóddal:
Amikor több mint egy [] operátor jelenik meg a kifejezésben, akkor az átaluk repreznetált tartományok méretének meg kell egyeznie.
Példák:
Tapasztalt FORTRAN numerikus programozók tudják, hogy egy többdimenziós négyzetes tömb olyan dolgokhoz, mint mátrix műveletek, sokkal gyorsabbak elérés szempontjából, mint olyan adatszerkezeten keresztül, ahol több egydimenziós tömbre mutató mutatókat csoportosítunk egy egydimenziós tömbbe.
Az előző kód egy olyan tömböt hoz létre, amelynek elemei mutatók egydimenziós tömbökre. (A dinamikus tömbök egy adatvektorra mutató mutatóval vannak megvalósítva.) Mivel ezek a tömbök változó méretűek lehetnek (mivel dinamikusan méretezhetők), ez néha "kesztyűszerű" tömbnek van nevezve. A kód optimalizáció szempontjából még rosszabb, hogy a sorok néha egymásra is mutathatnak. Szerencsére a D rendelkezik statikus tömbökkel, amelyek bár ugyanazt a szintaktikát használják, fix négyzetes elhelyezéssel vannak megvalósítva.
Az előző kód olyan négyzetes mátrixot hoz létre, amely 3 sort és 3 oszlopot tartalmaz, folyamatosan elhelyezve a memóriában. Más nyelvekben, ezt többdimenziós tömbnek hívják és a következőképpen lehet deklarálni:
A statikus tömbök tulajdonságai a következők:
.sizeof | A tömb hosszának és az egyes elemek byte-ban mért méretének szorzatát adja vissza. |
.length | Visszaadja a tömbben található elemek számát. Ez egy fix érték statikus tömbök esetén. |
.ptr | Visszaadja a tömb els? elemére mutató mutatót. |
.dup | Létrehoz egy dinamikus tömböt a tömbbel megegyez? méretben, és az adatokat átmásolja bele. |
.reverse | Helyben megfordítja a tömböt. A tömböt adja vissza. |
.sort | Helyben rendezi a tömböt. A tömböt adja vissza. |
A dinamikus tömbök tulajdonságai a következők:
.sizeof | Visszaadja a dinamikus tömb referenciájának méretét, ami 8 egy 32 bites gépen. |
.length | Lekérdezi/beállítja az elemek számát a tömbben. |
.ptr | Visszaadja a tömb els? elemére mutató mutatót. |
.dup | Létrehoz egy dinamikus tömböt a tömbbel megegyez? méretben, és az adatokat átmásolja bele. |
.reverse | Helyben megfordítja a tömböt. A tömböt adja vissza. |
.sort | Helyben rendezi a tömböt. A tömböt adja vissza. |
Példák:
A dinamikus tömbök .length tulajdonsága állítható az = operátor balértékeként.
Ez azt eredményezi, hogy a tömb reallokálásra kerül, és a jelenlegi tartalom az új tömbbe lesz másolva. Amennyiben az új tömb mérete kisebb, akkor csak annyi elem lesz lemásolva, amennyi még elfér benne. Amennyiben a tömb mérete nagyobb, akkor a maradék elemek az elem típusának alapértelmezett módján lesz inicializálva.
A hatékonyság növelésének érdekében, futási időben mindig helyben próbáljuk a tömb méretét megnövelni, hogy elkerüljük az extra másolásokat. Mindig másolás lesz, ha az új méret nagyobb mint az eredeti és a tömböt nem a new operátorral allokáltuk vagy nem egy előző átméretezéssel hoztuk létre.
Ez azt jelenti, hogy egy tömbszelet, amely közvetlenül követi az átméretezett tömböt, akkor az átméretezett tömb elfedheti a szeletet, vagyis:
Ahhoz, hogy garantáljuk a másolást, használjuk a .dup tulajdonságot, hogy biztosak legyünk benne, hogy a tömb átmérezhető.
Ezek a szempontok szintén érvényesülnek a tömbök összefűzésénél a ~ és ~= operátorok segítségével.
Egy dinamikus tömb átméretezése egy viszonylag költséges művelet. Ezért, bár a következő módja a tömb feltöltésének működik, nem lesz túl hatékony:
Egy sokkal praktikusabb megközelítés minimalizálja az átméretezések számát:
Egy jó kezdőérték megtippelése művészet, de egy olyat könnyű, amely az esetek 99%-át fedi. Például, ha a felhasználótól parancssorból várunk bemenetet, akkor nem valószínű, hogy 80 karakternél hosszabb lesz.
Egy tömböt nullánál kisebb vagy a tömb méretével megegyező vagy nagyobb értékkel indexelni hiba. Amennyiben az index az indexhatárokon kívülre esik, akkor egy ArrayBoundsError kivétel keletkezik, ha futási időben derül ki a hiba, illetve fordítási időben szintaktikai hiba lesz. Egy program nem építhet arra, hogy az indexhatárok ellenőrzése megtörténik, vagyis a következő program hibás:
A ciklus helyesen:
Implementációs megjegyzés: A fordítóprogramnak kísérletet kell tennie arra, hogy fordítási időben felderítse az indexhatárokon túli indexelést, például:
Az indexhatárokat futási időben ellenőrző kódrészlet beillesztése ki- és bekapcsolható fordítási időben egy kapcsolóval.
Üres inicializálás akkor történik, ha az Initializer egy tömb számára void. Ez azt jelenti, hogy nincsen inicializálás, tehát a tömb tartalma ismeretlen lesz. Ez mindössze optimalizáció szempontjából hasznos. Az üres inicializálás egy haladó technika és csak akkor célszerű használni, ha a kód futásának megfigyelése során jelentős sebességnövekedés várható tőle.
Ez leginkább akkor hasznos, ha a tömb indexek felsorolással vannak megadva:
Amennyiben bármelyik tagja a tömbnek inicializálva van, akkor az összeset inicializálni kell. Ez azért szükséges, hogy az olyan tipikus hibák kiküszöbölésre kerüljenek, mint a felsorolási típus új értékkel kerül kiegészítésre, de nem lesz inicializálva a tömbben az új tag.
Bit vektor a következő módon hozható létre:
A tároláshoz szükséges memóriaméret implementáció függő. Implementációs megjegyzés: Intel CPU-kon ez az érték fel lesz kerekítve a következő 32 bit méretre.
Azaz a méret osztva az elemek számával nem ugyanaz, mint a (x.size / x.length) kifejezés értéke.
A D nyelv egyik újdonsága az asszociatív tömb. Az asszociatív tömböknek van indexük, de ennek nem kell feltétlenül integernek lennie, és maga a tömb lehet hézagos. Egy asszociatív tömb indexét kulcs-nak nevezzük.
Asszociatív tömböt úgy kell deklarálni, hogy a kulcs típusát [] között adjuk meg.
A delete operátorral lehet törölni elemet az asszociatív tömbből:
Ez nem a b["hello"] tartalmát törli, hanem a "hello" kulcsot az asszociatív tömbből!
Az InExpression mező egy logikai értéket ad vissza, aszerint hogy egy kulcs eleme-e egy asszociatív tömbnek vagy nem:
Függvény vagy void nem lehet típusa egy kulcsnak.
Pointerek átkonvertálása nem pointerekké és fordítva a D-ben nem megengedett. Ez megakadályozza, hogy pointereket úgy kezeljünk, mintha integerek lennének, mivel ez a fajta használat nagy zavart tud okozni a szemétgyűjtésben és a kód egyik gépről a másik gépre való átvitelében. Ha valóban erre lenne szükségünk, alkalmazzuk az union-t, ennek ellenére legyünk óvatosak, hogy a szemétgyűjtésben ne okozzon kárt.
A D-ben sok típus van, beépítettből és deriváltból egyaránt. Fárasztó lenne minden típuskonverziót külön kérni, így az implicit konverziós lépések automatikusan hajtódnak végre.
Egy typedef típus implicit konverzióval képződik le az alapját képező típusra, minden más esetben explicit konverzió szükséges. Például:
A következő típusok impliciten konvertálódnak át int típusúvá:
A typedef-el definiált típusok átkonvertálódnak az alapjukat képező típusokra.
A szokásos aritmetikai konverziók a bináris operátorok operandusait átkonvertálják egy egyszerű típussá. Az operandusoknak aritmetikai típusúaknak kell lenniük. A következő szabályok kerülnek alkalmazásra:
A D-ben nincs lehetőség arra, hogy egy pointer egy tagra mutasson, viszont egy hasznosabb elképzelést - amelyet delegátornak nevezünk - támogat a nyelv. A delegátor két adat aggregációját jelenti: egy objektum-referencia és egy függvény-pointer társítását. Az objektum-referencia formálja a this pointert amikor a függvény meghívódik.
A delegátor deklarációja hasonló a függvény-pointerhez, azzal a különbséggel, hogy a 'delegate' kulcsszót kell használni a 'function' helyett:
Egy delegátor inicializálása hasonlóan történik, mint a függvény-pointereknél:
Egy delegátort nem lehet inicializálni statikus függvénytaggal vagy nem függvénytaggal.
Egy delegátort a függvény-pointerekhez hasonló módon lehet meghívni:
A delegátok a tagfüggvényekre való hivatkozáson kívül általánosabb módon is használhatók, melyről bővebben a Függvények és delegátok részben írunk.
Minden adattípus és kifejezés rendelkezik néhány beépített tulajdonsággal, amelyeket le lehet kérdezni:
Az .init egy konstans kifejezést eredményez, ami kezdő érték lesz. Ha az .init-et egy típusnál alkalmazzuk, akkor ez lesz a kezdőértéke a típusnak. Ha változó vagy mező esetén használjuk, akkor a kifejezés a változó vagy mező kezdőértéke lesz. Például:
Statikus tömbök:
.sizeof | A tömb hosszának és az egyes elemek byte-ban mért méretének szorzatát adja vissza. |
.length | Visszaadja a tömbben található elemek számát. Ez egy fix érték statikus tömbök esetén. |
.ptr | Visszaadja a tömb els? elemére mutató mutatót. |
.dup | Létrehoz egy dinamikus tömböt a tömbbel megegyez? méretben, és az adatokat átmásolja bele. |
.reverse | Helyben megfordítja a tömböt. A tömböt adja vissza. |
.sort | Helyben rendezi a tömböt. A tömböt adja vissza. |
Dinamikus tömbök:
.sizeof | Visszaadja a dinamikus tömb referenciájának méretét, ami 8 egy 32 bites gépen. |
.length | Lekérdezi/beállítja az elemek számát a tömbben. |
.ptr | Visszaadja a tömb els? elemére mutató mutatót. |
.dup | Létrehoz egy dinamikus tömböt a tömbbel megegyez? méretben, és az adatokat átmásolja bele. |
.reverse | Helyben megfordítja a tömböt. A tömböt adja vissza. |
.sort | Helyben rendezi a tömböt. A tömböt adja vissza. |
Asszociatív tömbök:
sizeof | Visszaadja az asszociatív tömb méretét, ami tipikusan 8 hosszú. |
length | Visszaadja a tömbbeli elemek számát. A dinamikus tömbökkel ellentétben ez csak olvasható. |
keys | Egy dinamikus tömböt ad vissza, melynek elemei az asszociatív tömb kulcsai lesznek. |
values | Egy dinamikus tömböt ad vissza, melynek elemei az asszociatív tömb elemei lesznek. |
rehash | Átalakítja az asszociatív tömböt úgy, hogy a keresés hatékonyabb legyen. Hasznos például akkor, amikor egy program megtölti a szimbólumtáblát, és most sürg?sen meg kell keresnie benne egy adatot. A rehash az átalakított tömbre ad egy referenciát eredményül. |