Datatype

Uit Wikipedia, de vrije encyclopedie
Ga naar: navigatie, zoeken

Een datatype, ook wel gegevenstype genoemd, is in de informatica een specifiek soort gegevens, zoals integers, booleans, reals, karakters, strings. In een programmeertaal wordt met iedere variabele, en meer in het algemeen met iedere expressie, een datatype geassocieerd. Dit datatype bepaalt welke waarden de variabele of de expressie kan aannemen, hoe deze waarden in het geheugen worden opgeslagen en welke bewerkingen op de variabele of de expressie uitgevoerd kunnen worden.

Bij elk gegevenstype hoort een verzameling van de mogelijke waarden die een variabele of expressie van dat type kan aannemen. Ook hoort er een systeem bij voor de codering van de waarden. Een waarde kan alleen al binnen één programmeertaal soms door meerdere datatypes gerepresenteerd worden: zo kan het getal 5 gerepresenteerd worden door diverse typen integer en real (er zijn vaak meerdere types van elk, met verschillende aantallen bits).

Expressies[bewerken]

Een expressie heeft hetzelfde datatype als het resultaat van de expressie. In sommige programmeertalen wordt het type van het resultaat geheel bepaald door de bewerking en de typen van de operanden, soms ook door hun waarden. Een deling van twee integers levert bijvoorbeeld in sommige programmeertalen altijd een real op, terwijl in andere dit ervan afhangt of de rekenkundige uitkomst een geheel getal is.

Indeling[bewerken]

Gegevenstypes kunnen worden onderscheiden in primitieve (primitive), enkelvoudige (simple) en samengestelde (complex) types. Primitieve datatypes vormen de basis voor de definities van andere gegevenstypes.

Primitief type[bewerken]

Een primitief type wordt door de taal zelf gedefinieerd en kan niet beschreven worden in termen van een ander datatype. In C, bijvoorbeeld, zijn char, int en float primitieve types.

Algemeen voorkomende primitieve typen zijn:[1][2]

  • Boolean, ook bekend als bool, flag of logic. Kan de waarde ja of nee bevatten. Andere benamingen voor deze waarden zijn waar en onwaar, of, gebruikelijker, het Engelse true en false.
  • Karakter, ook bekend als character of char. Deze kan precies één ANSI- of EBCDIC- of Unicode-teken bevatten. Het aantal bytes dat dit type inneemt hangt af van de taal. Historisch was dat meestal 1 byte, maar tegenwoordig ondersteunen veel talen Unicode en worden er meer bytes gereserveerd voor een variabele van type char.
  • Integer, ook bekend als int, short, long, signed is de representatie voor gehele getallen, hoewel moet worden benadrukt dat de twee niet hetzelfde zijn, een integer heeft nu eenmaal een beperkt bereik, terwijl een geheel getal dat niet heeft. Meestal worden definities zo gekozen dat een integer in een register past, maar dit is geen wet van Meden en Perzen, aangezien bewerkingen op een 64-bit integer vrij gemakkelijk kunnen worden verdeeld in twee 32-bit bewerkingen of zelfs 4 16-bit bewerkingen.
  • Real, ook bekend als float, single, double; alle niet gehele getallen. Voor het bereik van een real geldt hetzelfde als dat van een integer.
  • Decimal, ook bekend als fixed. Kan een vast aantal cijfers voor en achter de komma bevatten.
  • Void, ook wel bekend als null of unit. Het type void heeft geen waarde. Dit type duidt het ontbreken van een waarde aan.[3][4]

Van elk datatype bestaan wel varianten. De varianten kunnen verschillen in precisie (aantal bytes), interne representatie (in het geheugen) of de functies die erop toegepast kunnen worden. Bekende varianten van het type integer zijn unsigned, zonder teken, dus alleen niet negatieve waarden, en signed, met teken, zodat ook negatieve waarden mogelijk zijn.

Verschillende types kunnen in elkaar omgezet worden door middel van typeconversie. In sommige gevallen kan dit zonder dat er informatie verloren gaat (bijvoorbeeld bij het omzetten van een integer naar een real). In andere gevallen kan er informatie verloren gaan (bijvoorbeeld bij het omzetten van een real naar een integer).

Het type void wordt soms gebruikt in talen die geen onderscheid kennen tussen procedures en functies. In zulke talen (zoals C en Java) heeft een functie die de waarde van het type void oplevert, hetzelfde gedrag als een procedure. In functionele talen wordt het type void (vaak union genoemd) gebruikt voor expressies die een neveneffect bewerkstelligen (bijvoorbeeld in OCaml[5]).

Een variabele van een primitief type wordt vaak naar zijn type vernoemd. Zo noemt men een variabele van het type integer meestal een integer.

Enkelvoudig type[bewerken]

Een enkelvoudig type is een primitief type of een type dat op basis van een primitief type door de programmeur gedefinieerd is. Het criterium daarbij is dat een enkelvoudig gegevenstype uitsluitend als geheel kan worden gemanipuleerd en uitgelezen.

Bijvoorbeeld kan een type foo worden gedefinieerd als int.

typedef int foo;

Dit is geen primitief type, omdat het door de gebruiker is gedefinieerd, maar het is wel een enkelvoudig (simple) type.

Strings[bewerken]

Nuvola single chevron right.svg Zie String (informatica) voor het hoofdartikel over dit onderwerp.

Strings zijn reeksen van karakters. Er zijn verschillende manieren om strings te representeren.

  • Als primitieve waarden.
  • Als arrays van karakters (voorbeeld: Ada)
  • Als Pointers naar arrays van karakters (voorbeeld: C).
  • Als lijsten (lists) van karakters (voorbeeld: Haskell, zie strings in Haskell). Merk op dat een lijst een ander datatype is dan een array.)
  • Als objecten (voorbeeld: Java).

Samengesteld type[bewerken]

Een samengesteld (complex) type bestaat uit meerdere simpele types die afzonderlijk kunnen worden gemanipuleerd en uitgelezen. Hierbij valt te denken aan structuren, arrays en lijsten die samengesteld zijn uit elementen die op zichzelf ook kunnen bestaan uit samengestelde types: lijsten van lijsten, bijvoorbeeld, of geneste structuren. In tegenstelling tot primitieve types, die slechts in een beperkt aantal soorten voorkomen, is het aantal mogelijke samengestelde types in principe onbeperkt. Voorbeelden van benamingen voor samengestelde types in verschillende programmeertalen zijn: array, class, struct en record.

Afbeeldingen (mappings)[bewerken]

Een afbeelding f voegt aan elementen van verzameling A een element uit verzameling B toe. We schrijven: f : A \rightarrow B.

In programmeertalen komen we twee soorten afbeeldingen tegen: functies en arrays.

Arrays[bewerken]
Nuvola single chevron right.svg Zie Array voor het hoofdartikel over dit onderwerp.

De eenvoudigste vorm van een array is een geïndiceerde verzameling variabelen van een bepaald datatype. In de praktijk vormen de indices vaak een reeks van opeenvolgende discrete waarden. Het laagste element van de indices is de ondergrens, het hoogste element de bovengrens. De lengte van de array is het aantal elementen in de indexverzameling.

Veel talen beperken de indeces tot integers, eventueel beginnend bij 0, of met 0 als ondergrens. Er zijn echter ook talen die de programmeur vrij laten in het kiezen van het type van de indices, zolang dit maar een discreet primitief type is (voorbeeld: Ada).

De meeste talen ondersteunen ook meerdimensionale arrays. Voor een n-dimensionaal array is de index zelf een arrray met de n indices als elementen (n-tupel).

Associatieve array[bewerken]
Nuvola single chevron right.svg Zie Associatieve array voor het hoofdartikel over dit onderwerp.

Een array is in feite een speciale vorm van een associatieve array. Bij een associatieve array hoeft de index geen opeenvolgende reeks te zijn. Vaak is het ook toegestaan om andere (primitieve) typen dan integers als index te gebruiken, bijvoorbeeld strings.

Sommige talen ondersteunen wel 'pure' arrays, maar geen associatieve arrays (bijvoorbeeld C en Java). Andere talen hebben alleen associatieve arrays en implementeren gewone arrays als associatieve arrays (bijvoorbeeld PHP). Dit heeft een aantal nadelen op het gebied van efficiëntie en geheugenbruik. Er zijn ook talen die beide ondersteunen (bijvoorbeeld Perl).

Functies[bewerken]

Een functie is een afbeelding van een verzameling A, nu meestal argument of origineel genoemd, op een verzameling B, beeld of functiewaarde genoemd. Als er sprake is van een functie met meerdere argumenten, bestaat A weer uit tuples. In tegenstelling tot bij arrays, kan bij functies de bronverzameling A oneindig zijn.

Afgezien van het feit dat bij een functie de bronverzameling oneindig kan zijn is het belangrijkste verschil een kwestie van implementatie.

Structures, records en tupels[bewerken]

Veel programmeertalen maken het mogelijk om een nieuw type te definiëren dat bestaat uit (andere) typen. Er zijn verschillende termen voor zo'n samengesteld datatype: records (bijvoorbeeld in Pascal), structures of structs (bijvoorbeeld in C) of tupels (bijvoorbeeld in functionele programmeertalen zoals Haskell.

Ongeacht de benaming kan dit type gedefinieerd worden als een tupel van zijn samenstellende typen. Een record (of struct) van de typen A en B is een type T waarvoor geldt: T = \{(a,b)| a\in A, b\in B\}.

Oftewel: records, structs en tupel types zijn een Cartesisch product van hun samenstellende typen. Neem het volgende Pascal fragment:

 type
 t = record
 a: Integer;
 b: Char
 end;

Een variabele van type t kan alle waarden aannemen die bestaan uit een Integer gevolgd door een Char. Elk van deze waarden kan beschreven worden door een tupel (x, y) waarbij x een integer is en y een char. Het verschil tussen records en structures enerzijds en tupels anderzijds, is dat bij de eerste de samenstellende waarden kunnen worden aangeduid met een naam. De samenstellende delen van tupels worden aangeduid met hun positie.

Wat als we twee typen definiëren, die uit dezelfde samenstellende delen bestaan, zoals in onderstaand Pascal fragment?

 type
 t1 = record
 a1: Integer;
 b1: Char
 end;
 t2 = record
 a2: Integer;
 b2: Char
 end;

Er zijn nu 2 typen gedefinieerd, die beide uit een tupel van een integer en char bestaan. Dat wil zeggen: voor iedere (x, y) waarvoor geldt (x,y) \in T_1, geldt ook (x,y) \in T_2. De typen t1 en t2 zijn structureel equivalent. Toch staan niet alle talen het toe dat twee structureel equivalente typen door elkaar gebruikt worden. Het volgende Pascal fragment zal een error opleveren:

 var
 v1, v2: t1;
 v3: t2;
 begin
 v1.a1 := 3;
 v1.b1 := 'z';
 v2 := v1;
 v3 := v1;
 end.

De reden hiervan is dat Pascal gebruik maakt van name equivalence.

Klassen en objecten[bewerken]
Nuvola single chevron right.svg Zie Class en Object voor meer informatie over objectgeoriënteerd programmeren.

In een objectgeoriënteerde programmeertaal vormt iedere class een samengesteld type. De verzameling waarden die bij een gegeven class type horen, zijn alle objecten die een instantie van deze klasse zijn.

Wat hun structuur betreft verschilt een object niet van struct of records types. Een object is een record dat de waarden bevat die uniek zijn voor iedere instantie van dezelfde class, de attributen(of properties). Naast deze attributen definieert een class ook een aantal functies die op objecten van die class uitgevoerd kunnen worden, maar deze worden niet bij ieder object opgeslagen. Het grote verschil tussen objecten en 'gewone' records zit in de implementatie en de manier waarop objecten en classes gebruikt kunnen worden.

Boxing[bewerken]
Nuvola single chevron right.svg Zie Boxing voor het hoofdartikel over dit onderwerp.

In objectgeöriënteerde programmeertalen kan het handig zijn primitieve types te verpakken in objecten; dit wordt boxing genoemd. Deze objecten bevatten dan alleen de waarde van het primitieve type. Dit kan handig zijn wanneer men de primitieve waarden op dezelfde wijze wil gebruiken als objecten.

Name equivalence en structural equivalence[bewerken]

In het bovenstaande voorbeeld van records in Pascal zijn er twee typen gedefinieerd met dezelfde structuur. Een geldige waarde voor het ene type is per definitie een geldige waarde voor het andere type. Of het toegestaan is om deze types door elkaar te gebruiken hangt af van de manier waarop een taal bepaald of twee typen equivalent zijn.

Als een taal structural equivalence gebruikt, zijn twee types gelijk als ze dezelfde verzameling waarden hebben. Of dit het geval is, wordt bepaald door de structuur van het samengestelde type te vergelijken. Bij name equivalence wordt er gekeken of de twee typen op dezelfde plek gedefinieerd zijn, dat wil zeggen: of ze dezelfde naam hebben.

Een voorbeeld van een taal die name equivalence hanteert is Pascal. Daarom is het in het bovenstaande voorbeeld niet toegestaan om een variabele van het ene type toe te wijzen aan een variabele van het andere type, ook al zijn de twee typen op de naam na equivalent.

C kent beide soorten: op nieuwe types die gedefinieerd worden met enum, struct of union wordt name equivalence toegepast. Op nieuwe types die gedefinieerd worden met typedef wordt structural equivalence toegepast.[6]

Zie ook[bewerken]

Bronnen, noten en/of referenties

Voetnoten

  1. Sebesta, pagina 253 e.v.
  2. Watt, pagina 17 e.v.
  3. Aho, pagina 371
  4. Watt, pagina 23
  5. http://caml.inria.fr/pub/docs/oreilly-book/html/book-ora015.html#@fonctions49
  6. Watt, pagina 42

Bronnen

  • Programming Language Design Concepts, David A. Watt, John Wiley & Sons, 2004, ISBN 0470853204
  • Concepts of Programming Languages, Robert W. Sebesta, Addison Wesley, 2005, ISBN 0321330250
  • Compilers; Principles, Techniques & Tools 2e editie, Aho, Lam, Sethi, Ullman, Addison Wesley, 2007, ISBN 0-321-48681-1