Datatype

(Doorverwezen vanaf Primitief type)

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 de programmeertaal 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, om decimale breuken zonder afrondingsfouten te representeren
  • 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.

Een type foo kan bijvoorbeeld 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
  Zie Tekenreeks voor het hoofdartikel over dit onderwerp.

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

  • Als een primitieve waarde.
  • Als array van karakters (Ada)
  • Als pointer naar een array van karakters (C).
  • Als lijst (lists) van karakters (Haskell, zie strings in Haskell). Merk op dat een lijst een ander datatype is dan een array.)
  • Als objecten (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   voegt aan elementen van verzameling   een element uit verzameling   toe. We schrijven:  .

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

  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 indices 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  -dimensionale array is de index zelf een array met de   indices als elementen ( -tupel).

Associatieve array
bewerken
  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 geheugengebruik. Er zijn ook talen die beide ondersteunen (bijvoorbeeld Perl).

Functies
bewerken

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

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

Structure, record en tupel

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: record (bijvoorbeeld in Pascal), structure of struct (bijvoorbeeld in C) of tupel (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   en   is een type   waarvoor geldt:  .

Dat wil zeggen: de typen 'record', 'structs' en 'tupel' zijn hetcartesisch 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   waarin   een integer is en   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   waarvoor geldt  , geldt ook  . 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 gebruikmaakt van name equivalence.

Klassen en objecten
bewerken
  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 record 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.

  Zie Boxing (informatica) voor het hoofdartikel over dit onderwerp.

In objectgeorië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.

De programmeertaal 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
Zie de categorie Data types van Wikimedia Commons voor mediabestanden over dit onderwerp.