Indtastning af ordspil

Den aktuelle version af siden er endnu ikke blevet gennemgået af erfarne bidragydere og kan afvige væsentligt fra den version , der blev gennemgået den 19. oktober 2017; checks kræver 11 redigeringer .

Typepunning er et udtryk  , der bruges i datalogi til at henvise til forskellige teknikker til at krænke eller snyde typesystemet i et programmeringssprog , med en effekt, der ville være svær eller umulig at give i et formelt sprog .

C- og C++-sprogene giver eksplicitte skriveordspil gennem konstruktioner som casts , unionog også reinterpret_castfor C++ , selvom standarderne for disse sprog behandler nogle tilfælde af sådanne ordspil som udefineret adfærd .

I Pascal kan variantnotationer bruges til at fortolke en bestemt datatype på mere end én måde, eller endda på en måde, der ikke er hjemmehørende i sproget.

At skrive ordspil er en direkte overtrædelse af typesikkerheden . Traditionelt er evnen til at opbygge et skriveordspil forbundet med svag indtastningunsafe , men nogle stærkt indtastede sprog eller deres implementeringer giver sådanne muligheder (normalt ved at bruge ordene eller i deres tilknyttede identifikatorer unchecked). Tilhængere af typesikkerhed hævder, at " nødvendigheden " af at skrive ordspil er en myte [1] .

Eksempel: sockets

Et klassisk eksempel på et skriveordspil kan ses i Berkeley-socket -grænsefladen . En funktion , der binder en åben ikke-initialiseret socket til en IP-adresse , har følgende signatur:

int bind ( int sockfd , struct sockaddr * my_addr , socklen_t addrlen );

Funktionen bindkaldes normalt sådan:

struct sockaddr_insa = { 0 } ; int sockfd = ...; sa . sin_familie = AF_INET ; sa . sin_port = htons ( port ); bind ( sockfd , ( struktur sockaddr * ) & sa , sizeof sa );

Berkeley Sockets Library baserer sig dybest set på det faktum, at i C kan en pointer til struct sockaddr_inlet konverteres til en pointer til struct sockaddr, og at de to strukturtyper overlapper hinanden i deres hukommelsesorganisation . Derfor vil en markør til et felt my_addr->sin_family(hvor my_addrhar type struct sockaddr* ) faktisk pege på et felt sa.sin_family(hvor sahar type struct sockaddr_in ). Med andre ord bruger biblioteket et skriveordspil til at implementere en primitiv form for arv . [2]

I programmering er det almindeligt at bruge strukturer - "lag", der giver dig mulighed for effektivt at gemme forskellige typer data i en enkelt hukommelsesblok . Oftest bruges dette trick til gensidigt udelukkende data med henblik på optimering .

Eksempel: flydende kommatal

Antag, at du vil kontrollere, at et flydende kommatal er negativt. Man kunne skrive:

bool er_negativ ( float x ) { returnere x < 0,0 ; }

Flydende-komma-sammenligninger er imidlertid ressourcekrævende, fordi de fungerer på en særlig måde for NaN . I betragtning af at typen floater repræsenteret i henhold til IEEE 754-2008 standarden , og typen inter 32 bit lang og har samme fortegnsbit som i , kan du bruge et indtastningsordspil til at udtrække fortegnsbitten af ​​et flydende kommatal ved kun at bruge heltal sammenligning: float

bool er_negativ ( float x ) { return * (( int * ) & x ) < 0 ; }

Denne form for at skrive ordspil er den farligste. Det foregående eksempel var kun baseret på garantierne givet af C-sproget med hensyn til strukturrepræsentation og pointer - konverterbarhed ; Dette eksempel er dog baseret på specifikke hardwareantagelser . I nogle tilfælde, såsom ved udvikling af realtidsapplikationer , som compileren ikke er i stand til at optimere på egen hånd, viser det sig, at sådanne farlige programmeringsbeslutninger er nødvendige. I sådanne tilfælde hjælper kommentarer og kompileringstidstjek ( Static_assertions ) med at sikre kodevedligeholdelse . 

Et rigtigt eksempel kan findes i Quake III -koden - se Fast Inverse Square Root .

Ud over antagelserne om den bitvise repræsentation af flydende kommatal, overtræder ovenstående eksempel på et skriveordspil også reglerne fastsat af C-sproget for adgang til objekter [3] : xdet erklæres som float, men dets værdi læses i en udtryk, der har type signed int . På mange almindelige platforme kan dette ordspil med markørindtastning føre til problemer, hvis pointerne er forskelligt justeret i hukommelsen . Desuden kan pointere af forskellig størrelse dele de samme hukommelsesplaceringer , hvilket fører til fejl , der ikke kan opdages af compileren .

Brug af union

Problemet med aliasing kan løses ved at bruge union(selvom eksemplet nedenfor er baseret på den antagelse, at det flydende decimaltal er repræsenteret af IEEE-754 standarden ):

bool er_negativ ( float x ) { union { usigneret int ui ; flyde d ; } min_union = { . d = x }; return ( my_union . ui & 0x80000000 ) != 0 ; }

Dette er C99 -kode, der bruger Designated initialisers .  Når en union oprettes , initialiseres dens rigtige felt, og derefter læses værdien af ​​hele feltet (fysisk placeret på den samme adresse i hukommelsen) i henhold til paragraf s6.5 i standarden. Nogle compilere understøtter sådanne konstruktioner som en sprogudvidelse, såsom GCC [4] .

For et andet eksempel på et skriveordspil, se Stride of an array   .

Pascal

Variantnotation giver dig mulighed for at overveje datatypen på forskellige måder, afhængigt af den angivne variant. Følgende eksempel antager integer16 bit longintog real32 bit og character8 bit:

type variant_record = record case rec_type : longint af 1 : ( I : array [ 1..2 ] af heltal ) ; _ _ 2 : ( L : longint ) ; 3 : ( R : ægte ) ; 4 : ( C : array [ 1 .. 4 ] tegn ) ; _ ende ; Var V : Variant_record ; K : Heltal ; L.A .: Longint ; RA : Virkelig ; Ch : karakter ; ... V . I := 1 ; Ch := V . C [ 1 ] ; (* Hent den første byte i VI-feltet *) V . R := 8,3 ; LA := V . L ; (* Gem reelt tal i heltalscelle *)

I Pascal konverterer kopiering af et reelt tal til et heltal det til en afrundet værdi. Denne metode konverterer imidlertid en binær flydende kommaværdi til noget, der har længden af ​​et langt heltal (32 bit), som ikke er identisk og måske endda er inkompatibelt med lange heltal på nogle platforme.

Sådanne eksempler kan bruges til mærkelige transformationer, men i nogle tilfælde kan sådanne konstruktioner give mening, for eksempel ved at beregne placeringen af ​​bestemte datastykker. Følgende eksempel antager, at markøren og det lange heltal er 32 bit:

Type PA = ^ Arec ; Arec = record case rt : længde 1 : ( P : PA ) ; 2 : ( L : Longint ) ; ende ; Var PP : PA ; K : Longint ; ... Nyt ( PP ) ; P.P. ^. P := PP ; Writeln ( 'Variablen PP er placeret i hukommelsen ved' , hex ( PP ^. L )) ;

Standardproceduren Newi Pascal er beregnet til dynamisk at allokere hukommelse til en pointer, og hexer underforstået af en procedure, der udskriver en hexadecimal streng, der beskriver værdien af ​​et heltal. Dette giver dig mulighed for at få vist adressen på markøren, hvilket normalt er forbudt (pointere i Pascal kan ikke læses eller udlæses - kun tildeles). At tildele en værdi til en heltalsvariant af en pointer giver dig mulighed for at læse og ændre ethvert område af systemhukommelsen:

P.P. ^. L : = 0 PP := PP ^. P ; (* PP peger på adresse 0 *) K := PP ^. L ; (*K indeholder værdien af ​​ordet på adresse 0 *) Writeln ( 'Ordet på adresse 0 på denne maskine indeholder' , K ) ;

Dette program kan fungere korrekt eller gå ned, hvis adresse 0 er læsebeskyttet, afhængigt af operativsystemet.

Se også

Noter

  1. Lawrence C. Paulson. ML for den arbejdende programmør. — 2. - Cambridge, Storbritannien: Cambridge University Press, 1996. - S. 2. - 492 s. - ISBN 0-521-57050-6 (hardcover), 0-521-56543-X (softcover).
  2. struct sockaddr_in, struct in_addr . www.gta.ufrj.br. Dato for adgang: 17. januar 2016. Arkiveret fra originalen 24. januar 2016.
  3. ISO/IEC 9899:1999 s6.5/7
  4. GCC: Ikke-fejl . Hentet 21. november 2014. Arkiveret fra originalen 22. november 2014.

Links