L'intero è usato troppo come tipo di dati?


9

La maggior parte degli sviluppatori di applicazioni utilizza numeri interi con segno in luoghi in cui intendono realmente utilizzare numeri interi senza segno? Lo faccio sempre, così come i miei collaboratori. Non ho visto molti altri codebase estesi (diversi da Delphi VCL) e gli esempi su Internet di solito usano numeri interi. Considerando che gli sviluppatori VCL utilizzano i propri tipi di dati (che sarebbe il modo più non pigro per dichiarare le variabili).

Qualcosa sembra un po 'spaventoso di codice come questo

TStuffRec = record
   recordID : Integer;
   thingID : Integer;
   otherThingID : Integer;
end;

quando potrebbe essere scritto come

TStuffRec = record
   recordID : Cardinal;
   thingID : Cardinal;
   otherThingID : Cardinal;
end;

Funzionalmente, questi record funzionano quasi sempre allo stesso modo (e si spera continueranno a funzionare allo stesso modo anche con Delphi a 64 bit). Ma numeri molto grandi avranno problemi di conversione.

Ma ci sono anche degli svantaggi nell'uso di ints senza segno. Principalmente derivante da quanto sia fastidioso mescolare i due.

La vera domanda è: è questa una cosa che viene effettivamente pensata o inclusa nelle migliori pratiche? Di solito dipende solo dallo sviluppatore?


5
Peter, stai cercando solo risposte specifiche per Delphi?
Adam Lear

3
@Anna Comprendere come funzionano i tipi di dati Delphi sarebbe una risposta eccellente. Sono ragionevolmente sicuro che i programmatori C potrebbero comprendere e rispondere a questa domanda.
Peter Turner,

Risposte:


9

Uno dei motivi per cui non uso così tanto i tipi interi senza segno in Delphi è che possono creare problemi se mescolati con numeri interi con segno. Eccone uno che mi ha morso una volta:

for i := 0 to List.Count - 1 do
  //do something here

Avevo idichiarato come numero intero senza segno, (dopo tutto, è un indice in un elenco che inizia da 0, non deve mai essere negativo, giusto?), Ma quando List.Countfosse 0, non avrebbe cortocircuito il ciclo come previsto perché 0 - 1valuta un numero positivo davvero elevato. Oops!

Tra i potenziali problemi di sicurezza inerenti alla miscelazione di numeri interi con o senza segno e i problemi relativi all'intervallo (se hai bisogno di numeri positivi maggiori di high(signed whatever), è molto probabile che finirai anche per avere bisogno di numeri positivi più grandi di high(unsigned whatever)così, quindi spostando fino alla successiva dimensione più grande invece di passare da firmato a non firmato della stessa dimensione è di solito l'azione corretta). In realtà non ho trovato troppi usi per numeri interi senza segno quando rappresentano la maggior parte dei dati.


2
In qualche modo correlato, uno dei maggiori rischi dell'utilizzo di un tipo di dati potenzialmente più piccolo del necessario (al contrario di semplicemente non firmato rispetto a quello firmato) è che se la condizione di uscita è maggiore di quanto pianificato, si può effettivamente finire con un ciclo infinito mentre il contatore trabocca ancora e ancora. Sembra stupido col senno di poi, ma una volta ho scritto un programma che avrebbe dovuto scorrere tutti i possibili valori di byte, e ci sono voluti circa 15 minuti per convincermi finalmente che non era possibile fare con un contatore di byte.
Aaronaught,

@Aaronaught: Non a Delfi. (Almeno a meno che tu non faccia qualcosa di stupido come disabilitare il controllo di overflow incorporato.) Finirai con un'eccezione quando il contatore trabocca, invece di un ciclo infinito. È ancora un bug, ma è molto più facile rintracciare.
Mason Wheeler,

Se lo dici tu. Ho sempre disabilitato il controllo di overflow in Delphi; dopo essere stato bombardato all'infinito con falsi positivi da cose come codici hash e checksum, ho rinunciato completamente a quella "caratteristica". Ma suppongo che tu abbia ragione, avrebbe colto quell'errore specifico.
Aaronaught,

@Aaronaught: Beh, sì, vorresti disabilitarlo per cose come codici hash e checksum che sono specificamente progettati per traboccare e avvolgere. Ma per i calcoli di uso generale che non sono progettati per traboccare e avvolgere, è un'importante funzione di sicurezza e disattivarlo è un po 'come guidare senza una cintura di sicurezza.
Mason Wheeler,

Forse te ne sei dimenticato, ma le direttive sul controllo dell'overflow e sul compilatore erano incredibilmente difettose nelle versioni precedenti di Delphi. Ricordo vividamente di avermi strappato i capelli in più occasioni dopo aver visto il debugger fermarsi direttamente nel mezzo di un blocco {$ O -} / {$ O +} per segnalare allegramente un overflow. Dopo un po 'non ce la facevo più e l'ho disabilitata a livello globale. Ancora una volta, sì, avrebbe riscontrato questo problema, ma non credo ancora che valga il numero di falsi positivi. A ciascuno il suo, ovviamente!
Aaronaught il

3

Ad essere sincero, tendo a usare Integer per abitudine. Mi sono abituato al fatto che offrono intervalli abbastanza grandi per la maggior parte delle situazioni e consentono valori negativi (come -1). In effetti, molte volte l'uso di byte / parola / shortint sarebbe più appropriato. Ora a pensarci posso concentrarmi su questi punti:

  • Prospettiva. La dimensione di Tilemap è limitata a riquadri 192x192, quindi potrei usare byte per indirizzare riquadri e loop. Ma se si desidera aumentare le dimensioni della mappa, dovrò passare ogni uso e sostituirlo con, ad esempio, la parola. Quando devo consentire gli oggetti fuori mappa dovrei andare di nuovo per passare a smallint.

  • Loops. Spesso scrivo un ciclo "da i: = 0 a Count-1", cosa succede se "i" è byte e Count = 0 è che il ciclo va da 0 a 255. Non che lo vorrei.

  • Uniformante. È più facile da ricordare e applicare "var i: integer;" che fermarsi in ogni caso e pensare "Hm .. qui abbiamo a che fare con un intervallo 0..120 .. byte .. no, aspetta, potremmo aver bisogno di -1 per non inizializzato .. shortint .. aspetta .. e se 128 è non abbastanza .. Arrgh! " o "Perché è piccolo in questo posto, non un difetto?"

  • Combinando. Quando devo combinare due o più classi insieme, potrebbero utilizzare tipi di dati diversi per i loro scopi, l'utilizzo di tipi più ampi consente di saltare conversioni non necessarie.

  • -1. Anche quando i valori sono compresi nell'intervallo 0..n-1, spesso ho bisogno di impostare il valore "nessun valore / sconosciuto / non inizializzato / vuoto", che è di solito pratica -1.

L'uso di Integer consente di saltare tutti questi problemi, dimenticare l'ottimizzazione di basso livello laddove non è necessaria, passare a un livello superiore e concentrarsi su problemi più reali.

PS Quando uso altri tipi?

  • Contatori, non sono mai negativi e sono di sola lettura al di fuori della loro classe.
  • Per motivi di prestazioni / memoria, forzare l'uso di tipi di dati più brevi in ​​determinati luoghi.

1

La migliore pratica consiste nell'utilizzare un tipo di dati adatto alle esigenze dei dati utilizzati (dati previsti).

Esempi C #: se avessi solo bisogno di supportare da 0 a 255, avrei usato un byte.

Se avessi bisogno di supportare 1.000.000 di negativi e positivi, allora int.

Più grande di 4,2 miliardi, quindi utilizzare a lungo.

Scegliendo il tipo corretto, il programma utilizzerà la quantità ottimale di memoria, così come tipi diversi utilizzano quantità diverse di memoria.

Ecco un riferimento C # int da MSDN.

int 
 -2,147,483,648 to 2,147,483,647
 Signed 32-bit integer

uint 
 0 to 4,294,967,295
 Unsigned 32-bit integer

long 
 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 Signed 64-bit integer

ulong 
 0 to 18,446,744,073,709,551,615
 Unsigned 64-bit integer

In C # (o .net in generale) sarebbero lunghi e ulong diventati 128 bit su una macchina a 128 bit? Perché in Delphi, il Integertipo di dati è 32 bit su una macchina a 32 bit e apparentemente sarà 64 bit su una macchina a 64 bit.
Peter Turner,

1
@Peter Turner: No, in C # intè solo una scorciatoia per System.Int32, indipendentemente dalla macchina su cui viene eseguito il codice.
nikie il

@nikie, è proprio type int System.Int32o qualcosa del genere? Potrebbe essere cambiato così facilmente in una versione futura del framework?
Peter Turner,

@Peter Turner / nikie (sizeof (int) .ToString ()); ==> Restituisce 4 (sizeof (Int64). ToString ()); ==> Restituisce 8 Sul mio sistema operativo Windows a 64 bit. Come nikie, stats, un int è davvero giusto e Int32.
Jon Raynor,

1
Una cosa da notare è che non tutti i tipi sono conformi alle specifiche del linguaggio comune . uintè uno di questi tipi non conformi, il che significa che non dovrebbe essere usato nell'API esposta pubblicamente per evitare di interrompere la possibilità di usare quell'API in linguaggi .NET diversi da quello in cui è scritta la libreria. Questo è anche il motivo per cui il framework .NET L'API stessa sta usando intdove uintfarebbe.
Adam Lear

1

I tipi interi senza segno devono essere usati solo per rappresentare i numeri cardinali nelle lingue in cui rappresentano i numeri cardinali. A causa del modo in cui i computer che eseguivano C funzionavano, i tipi interi senza segno si comportavano come membri di anelli algebrici mod-2 ^ n (il che significa che i calcoli traboccati si "avvolgevano" prevedibilmente) e il linguaggio specifica che in molti casi tali tipi sono richiesto di comportarsi come anelli algebrici astratti anche quando tale comportamento sarebbe incompatibile con il comportamento di numeri cardinali o numeri interi matematici.

Se una piattaforma supportasse completamente tipi separati per numeri cardinali e anelli algebrici, suggerirei che i numeri cardinali debbano essere elaborati utilizzando i tipi di numero cardinale (e le cose che devono essere avvolte utilizzando i tipi di anello). Non solo questi tipi potevano memorizzare numeri due volte più grandi dei tipi firmati, ma un metodo che riceveva un parametro di tale tipo non avrebbe dovuto verificare se fosse negativo.

Data la relativa mancanza di tipi di numeri cardinali, tuttavia, è generalmente meglio semplicemente utilizzare numeri interi per rappresentare sia numeri matematici che numeri cardinali.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.