Indice Variabili e Tipi Fondamentali in C++ (CAP 2, parte I) • Tipi semplici propri del linguaggio Alberto Garfagnini e Marco Mazzocco • Variabili • Tipi composti Università degli studi di Padova • Il “qualifier” const • Come operare con i tipi A.A. 2014/2015 • Come definire le proprie strutture di dati Introduzione I tipi aritmetici • I tipi di dati sono un elemento fondamentale per ogni linguaggio di programmazione: ci permettono di definire il significato dei dati e quali operazioni su di essi sono consentite • Il C++ ha un supporto esteso sui tipi: 3 definisce svariati tipi primitivi 3 fornisce i meccanismi per estenderli e definirne di nuovi • Il linguaggio definisce un insieme di tipi fondamentali aritmetici per rappresentare 3 caratteri, interi, booleani e virgola mobile 3 un tipo speciale chiamato void Tipo bool char wchar_t char16_t char32_t short int long long long float double long double Descrizione booleano (true|false) carattere carattere esteso carattere Unicode carattere Unicode intero intero intero intero virgola mobile in singola precisione virgola mobile in doppia precisione virgola mobile in precisione estesa Dimensione Minima NA 8 bits 16 bits 16 bits 32 bits 16 bits 16 bits 32 bits 64 bits ≥ 6 cifre significative ≥ 10 cifre significative ≥ 10 cifre significative • Esistono tipi addizionali interi con un numero di bit fissato, definiti nel header file cstdint I bool e i char Gli interi • Gli interi possono essere: • Un bool può assumere due valori: true oppure false. bool is_ready = true; 3 con segno (signed) (rappresentati in base due, notazione Complemento 2) 3 dominio con n-bit : −2n−1 + 1, 2n−1 − 1 • Conversione bool → int: int ans = true; int promise = false; 3 senza segno (unsigned) (rappresentati in base due, con tutti i bit a disposizione per il valore assoluto del numero) • Conversione int → bool: 3 dominio con n-bit : [0, 2n − 1] Tipo short Bit (specs) ≥ 16 Bit 16 int ≥ short 32 ≥ 32 | int long 32 64 long long MIN/MAX -32768 32767 -214783648 214783647 -214783648 214783647 -9223372036854775808 9223372036854775807 //assume valore 1 // assume valore 0 unsigned short Tipo Bit (specs) ≥ 16 Bit 16 unsigned int ≥ short 32 bool start = -100; MAX 65535 bool stop = 0; 4294967295 unsigned long ≥ 32 | int 32 4294967295 • La rappresentazione dei caratteri segue il codice ASCII (8 bit) 64 18446744073709551615 unsigned long long //assume valore true // come tutti i valori // diversi da zero // assume valore false • Esistono tre tipi : char, signed char e unsigned char • Ci sono tipi interi con dimensioni fissate (header <cstdint>) : • int<N>_t per i tipi con segno e uint<N>_t per quelli senza segno • La rappresentazione (con o senza segno) dei char è lasciata libera dallo standard N = numero di bit del numero Tipo char C++11 3 int8_t, int16_t, int32_t, int64_t 3 uint8_t, uint16_t, uint32_t, uint64_t I tipi a virgola mobile Bit (specs) 8 Bit 8 MIN/MAX 0/-127 255/128 Sulla precisione dei tipi a virgola mobile • I numeri reali sono di tre tipi: #include <iostream> Singola precisione Doppia precisione Nuovo nello standard float; double; long double; • Notazione esponenziale (es. +5.37E+16) • La dimensione non è specificata, tipicamente float ≤ double ≤ long double Tipo Cifre significative Bit mantissa Float 6 24 FLT_DIG FLT_MANT_DIG Double Long Double 15 53 DBL_DIG DBL_MANT_DIG 18 64 LDBL_DIG LDBL_MANT_DIG MAX esponente/ MIN esponente +38 -37 FLT_MAX_10_EXP FLT_MIN_10_EXP +308 -307 DBL_MAX_10_EXP DBL_MIN_10_EXP +4932 -4931 LDBL_MAX_10_EXP LDBL_MIN_10_EXP int main( ) { using namespace std; cout.setf(ios_base::fixed, ios_base::floatfield); float tub = 10.0/3.0; // preciso a 6 cifre double mint = 10.0/3.0; // preciso a 15 cifre const float MEGA = 1.E6; cout << "tub = " << tub << endl; cout << "MEGA tub = " << tub*MEGA << endl; cout << "10MEGA tub = " << tub*MEGA*10 << endl; } tub = 3.333333 cout << "mint = " << mint << endl; cout << "MEGA mint = " << mint*MEGA « endl; cout << "10MEGA mint = " << mint*10*MEGA « endl; MEGA tub = 3333333.250000 return 0; MEGA mint = 3333333.333333 10MEGA tub = 33333332.000000 mint = 3.333333 10MEGA mint = 3333333.333333 Precisione finita dei tipi a virgola mobile // fltadd.cpp - esempio sulla precisione finita dei float #include <iostream> Quali tipi devo utilizzare ? • Il C++ non definisce strettamente la dimensione dei tipi di dati in modo da essere compatibile con diverso hardware • Alcune regole pratiche: int main( ) { float a = 2.34E22f; float b = a + 1.0f; 3 usare un tipo unsigned se sicuri che i valori non possono essere negativi std::cout << "a = " << a << std::endl; std::cout << "b - a = " << b - a << std::endl; return 0; } Il suffisso f indica una costante di tipo float Output del programma a = 2.34e+22 b - a = 0 La conversione tra tipi I La conversione tra tipi avviene automaticamente quando dato un oggetto, il programma si aspetta un oggetto di un tipo diverso bool b = 42; int i = b; i = 3.14; double pi = i; unsigned char c1 = -1; signed char c2 = 255; II III IV V VI VII // // // // // // b => true i => 1 i => 3 pi => 3.0 c1 => 255 con 8-bit chars c2 undefined Intero → bool ⇒ false se il valore è 0 e true altrimenti bool → intero ⇒ 1 se il bool è true e 0 se false Floating-point → intero ⇒ valore è troncato, parte decimale persa Intero → Floating-point ⇒ parte decimale è zero. Perdita di precisione possibile Valore out-of-range → unsigned ⇒ resto del valore, modulo dominio Valore out-of-range → signed ⇒ risultato indefinito unsigned char uc = 256; signed char sc = 256; $ g++ -std=c++11 uchar.C uchar.C:9:8: warning: large integer implicitly truncated to unsigned type [-Woverflow] uchar.C:16:8: warning: overflow in implicit constant conversion [-Woverflow] 3 usare int per tutta l’aritmetica tra interi short è solitamente troppo piccolo e long ha spesso la stessa dimensione di un int. Usare long long se il numero supera il dominio garantito dagli int 3 non usare char oppure bool in aspressioni aritmetiche L’utilizzo di char può essere problematico perchè su alcune architetture è di tipo signed e in altre unsigned 3 usare double per l’aritmetica a virgola mobile Sulle architetture odierne (processori a 64-bit) le operazioni a doppia precisione sono più veloci di quelle in singola. La precisione offerta dai tipi long double a volte non è necessaria (quindi il double è un risparmio computazionale Espressioni con tipi unsigned unsigned u = 10; int i = -42; std::cout << i + i << std::endl; std::cout << u + i << std::endl; // Stampa: -84 // Stampa: 4294967264 3 nella seconda espressione il numero negativo è convertito a unsigned prima dell’addizione unsigned u1 = 42, u2 = 10; std::cout << u1 - u2 << std::endl; std::cout << u2 - u1 << std::endl; // Stampa: 32 // Stampa: 4294967264 3 il fatto che un tipo unsigned non può essere mai minore di zero si riflette nelle scritture dei cicli for // Ciclo corretto: for (int i=10; i>=0; i--) { std::cout << i << std::endl; } // Errato: u e’ sempre >=0 ! for (unsigned u = 10; u >=0; u--) { std::cout << u << std::endl; } Le costanti L’ aritmetica degli interi • Ogni costante ha un tipo determinato dal suo formato e valore Reset point -32768 +32767 +32768 +32767 +49152 +16384 -16384 • Costanti intere, espresse in base 3 otto se iniziano con uno zero : (es 020) 3 sedici se iniziano con 0x oppure 0X : (es 0x20) 3 dieci (tutti gli altri) +16384 +65535 +1 0 • Costanti carattere: 3 sono indicate tra i singoli apici : (es ’a’) -1 +1 0 Interi unsigned • Costanti a virgola mobile: 3 hanno un punto (es 3.14) oppure un esponente che indica la notazione scientifica (3.14E-3, o 3.14e-3) 3 lo zero può essere scritto come 0., 0.0, 0e0, 0E0, . . . Interi con segno • Costanti stringa (come array di caratteri) : 3 sono indicate tra i doppi apici : (es "Hello World") Le sequenze di caratteri Escape Le variabili • Alcuni caratteri non hanno un immagine visibile (per esempio il carattere "a • Per poterli rappresentare viene utilizzata una sequenza di due caratteri : ’\’ + ’carattere’ newline vertical tab backslash carriage return • Una variabile fornisce un nome ad uno spazio di memoria che contiene dati che possono essere utilizzati nel programma capo") \n \v \\ \r horizontal tab backspace question mark formfeed \t \b \? \f alert double quote single quote \a \" \’ std::cout << ’\n’; // prints a newline std::cout << "\tHi!\n" // tab, text, plus a newline • È anche possibile scrivere una sequenza di caratteri escape generalizzata per rappresentare qualsiasi carattere: 7 \x seguito da uno o più cifre esadecimali (es \x4d) 7 \ seguito da uno o più cifre ottali (es \115) • il tipo della variabile determina la dimensione, la struttura della memoria utilizzata, il dominio dei valori immagazzinabili e l’insieme delle operazioni disponibili sulla variabile Tipo di dato identificatore TypeName variableName1, variableName2, . . . ; identificatore • La definizione può fornire un valore iniziale per gli elementi che definisce int sum = 0, value, // tre variabili di tipo int unit_sold = 0; // solo la I e la III sono iniz. a 0 Sales_item book; // oggetto della classe Sales_item // string e’ una classe della libreria standard del C++ std::string book("8804418052"); // book creato e iniz. std::cout << "\x4dO\115\12"; // prints MOM and a newline Un oggetto è una regione di memoria che ha un tipo Inizializzazioni di oggetti • Una oggetto inizializzato acquisisce un valore specifico nel momento in cui viene creato • si può inizializzate una variabile con una costante, un’espressione o con il valore di un’altra variabile double discount_rate = 0.20; double price = 30.50, discount = price * discount_rate; List Initialization • Il C++ definisce diversi modi per inizializzare una variabile int int int int • L’utilizzo di parentesi graffe per inizializzare una variabile è stata introdotta dal C++11 → prende il nome di list initialization • inizializzazioni e assegnazioni sono operazioni differenti in C++ • si parla di inizializzazione quando si crea una variabile e si inizializza ad un valore • con l’assegnazione si sovrascrive una variabile con un nuovo valore double rate = 0.20; double discount_rate; discount_rate = rate; // inizializzazione long double pi = 3.14.15926536; int a{ld}, b = {ld}; // ERRORE: perdita di informazione int c(ld), d = ld; // permesso, init con troncamento • Il compilatore non permetterà l’inizializzazione da liste di una variabile di C++11 // assegnazione zero = 0; zero = {0}; C++11 zero{0}; zero(0); tipi built-in se questo comporta la perdita di informazione Identificatori Default Initialization • Quando creiamo una variabile senza specificare un valore iniziale, la variabile è inizializzata al valore di default • Il valore di default dipende dal tipo della variabile e da dove è stata screata 3 variabili definite fuori da funzioni sono automaticamente inizializzate a zero 7 variabili definite nel corpo di funzioni non sono inizializzate 7 il valore di una variabile non inizializzato non è predicibile 3 Un identificatore (i.e. nome di oggetto) può essere composto da: 3 lettere, cifre e dal carattere underscore 3 non ci sono limiti sulla lunghezza del nome 3 gli identificatori sono case sensitive // definiamo 4 variabili distinte: int somename, someName, SomeName, SOMENAME; • le classi possono definire oggetti con o senza richiesta di inizializzazione. 3 Alcune classi richiedono che ogni oggetto sia inizializzato esplicitamente std::string global_string; int global_counter; int main () { int local_init; std::string local_string; ... } A // initialized // init to empty string • Ci sono un certo numero di convenzioni comunemente accettate per i nomi delle variabili. • Seguire una delle convenzioni migliora la leggibilità dei programmi • un identificatore deve riflettere il significato della variabile // undefined // init to empty string : Le variabili non inizializzate posso causare problemi RUN-TIME nell’esecuzione dei programmi • Regole di base: • gli identificatori sono principalmente in caratteri minuscoli • la libreria standard del C++ standard usa un carattere underscore come separatore di parole. Esempio: get_background • UpperCamelCase: GetBackground • lowerCamelCase: getBackground Visibilità delle variabili 3 3 3 3 3 3 Visibilità di una variabile = parte di codice all’interno della quale si può utilizzare normalmente la visibilità è delimitata dalle parentesi {} globale (global scope) per tutto ciò che è definito fuori dalle funzioni di blocco (block scope) per le altre variabili una variabile definita in global scope è visibile anche in block scope la visibilità può essere concatenata (nested scope) #include <iostream> global scope int reused = 42; int main () { local scope int unique = 0; std::cout << reused << " " << unique << std::endl; // 42 0 new objects, hides global reused int reused = 0; std::cout << reused << " " << unique << std::endl; // 0 0 std::cout << ::reused << " " << unique << std::endl; // 42 0 return 0; request global reused, explicitly } A : Bad Idea definire variabile locale e globale con lo stesso nome
© Copyright 2024 Paperzz