Lezione 5
Transcript
Lezione 5
Introduzione al C I puntatori I puntatori •I puntatori rappresentano uno degli aspetti più importanti e problematici del C. •La potenza del C è legata in larga misura ai puntatori. • Consentono l’allocazione dinamica della memoria e costituiscono un mezzo tramite il quale una funzione può modificare il contenuto di un argomento. Memoria vint 1000 1001 12252 vchar 1002 1003 vfloat 1004 1005 1006 90 1007 1008 1009 1010 1200.156004 int vint = 12252; char vchar = 90; float vfloat = 1200.156004; Quando una variabile viene dichiarata, il compilatore alloca spazio in memoria per contenere i valori della variabile. Dichiarazione e inizializzazione dei puntatori • Variabili puntatore – Contengono come valore degli indirizzi di memoria – Di norma, una variabile contiene direttamente un valore specifico (riferimento diretto) count 7 – Un puntatore contiene invece l’indirizzo di una variabile che contiene il valore specificato (riferimento indiretto) • Se per esempio la variabile p_vint contiene l’indirizzo della variabile vint allora si dice che p_vint “punta a” vint countPtr count 7 Dichiarazione di puntatori La forma generale di dichiarazione di un puntatore è: tipo *p_nomep_nome-variabile; variabile; dove •tipo indica il tipo di base del puntatore e deve essere un tipo C valido • p_nome-variabile è il nome della variabile puntatore. Per esempio per dichiarare p come puntatore ad un intero si utilizza questa dichiarazione: int *p; Il tipo di base di un puntatore determina il tipo di dato a cui punterà. Inizializzazione di puntatori Dopo aver dichiarato un puntatore, esso deve essere inizializzato, altrimenti non può essere usato – I puntatori possono essere inizializzati con 0, NULL, o con un indirizzo − 0 o NULL - il puntatore non farà riferimento a nessun dato (NULL è preferibile) File di intestazione <stddef.h> Operatori sui puntatori • & (operatore di indirizzo) – Restituisce l’indirizzo dell’operando int y = 5; int *yPtr; yPtr = &y; //assegna a yPtr l’indirizzo di y – yPtr “punta a” y yPtr y 5 yptr 500000 600000 y 600000 Il valore di yptr è l’indirizzo di y 5 …continua • * (operatore di deriferimento / rinvio / risoluzione del riferimento) – Restituisce il valore dell’oggetto puntato dal suo operando *yptr restituisce il valore di y (yptr punta a y) – * può essere usato nelle frasi di assegnazione *yptr = 7; // assegna 7 a y vint p_vint 12252 1002 1003 1004 1005 1006 90 int *p_vint; char *p_vchar; float *p_vfloat; 1007 1008 1009 1010 1200.156004 p_vfloat 1001 vfloat p_vchar 1000 vchar *p_vint vale 12252; *p_vchar vale 90; *p_vfloat vale 1200.156004; p_vint = &vint; &vint; p_vchar = &vchar; &vchar; p_vfloat = &vfloat; &vfloat; …continua • * e & sono l’uno il complemento dell’altro *&yptr -> * (&yptr) -> * (address of yptr)-> restituisce il valore a cui punta l’operando, ossia restituisce yptr &*yptr -> &(*yptr) -> &(y) -> restituisce l’indirizzo di y, che è yptr Utilizzo delle variabili puntatore int i = 20; int *p; int *p2; … p = &i; … p2 = p; … *p2 = 20; • In questo caso *p2 = 20 è tecnicamente equivalente a *p = 20 che a i = 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 /* Using the & and * operators */ #include <stdio.h> int main() { int a; int *aPtr; a = 7; aPtr = &a; /* a is an integer */ /* aPtr is a pointer to an integer */ /* aPtr set to address of a */ printf( "The address of a is %p" "\nThe value of aPtr is %p", &a, aPtr ); printf( "\n\nThe value of a is %d" "\nThe value of *aPtr is %d", a, *aPtr ); printf( "\n\nShowing that * and & are inverses of " "each other.\n&*aPtr = %p" "\n*&aPtr = %p\n", &*aPtr, *&aPtr ); return 0; } Esecuzione Esecuzione The The address address of of aa is is 0012FF88 0012FF88 The value of aPtr is The value of aPtr is 0012FF88 0012FF88 The The value value of of aa is is 77 The The value value of of *aPtr *aPtr is is 77 Proving Proving that that ** and and && are are complements complements of of each each other. other. &*aPtr = 0012FF88 &*aPtr = 0012FF88 *&aPtr *&aPtr == 0012FF88 0012FF88 Chiamata per riferimento delle funzioni • Chiamate per riferimento con puntatori come argomenti – Passare l’indirizzo dell’argomento usando l’operatore & – Ciò permette di modificare il contenuto delle locazioni in memoria – I vettori non sono passati con & perchè il nome del vettore è già un puntatore • operatore * – Usato come alias/nickname per la variabile all’interno della funzione void raddoppia(int *number) { *number = 2 * (*number); } *number è usato come nickname per la variabile passata 1 /* 2 Cube a variable using call-by-reference 3 with a pointer argument */ 4 5 #include <stdio.h> 6 7 void cubeByReference( int * ); /* prototype */ 8 9 è passato l’indirizzo di number cubeByReference aspetta un puntatore (l’indirizzo di una variabile). int main() 10 { 11 int number = 5; 12 13 printf( "The original value of number is %d", number ); 14 cubeByReference( &number ); 15 printf( "\nThe new value of number is %d\n", number ); 16 17 All’interno di cubeByReference, è usato *nPtr (*nPtr è number). return 0; 18 } 19 20 void cubeByReference( int *nPtr ) 21 { 22 *nPtr = *nPtr * *nPtr * *nPtr; /* cube number in main */ 23 } The The original original value value of of number number is is 55 The The new new value value of of number number is is 125 125 Utilizzare il qualificatore const con i puntatori • qualificatore const - la variabile non può essere modificata – Utile usare const se la funzione non ha bisogno di modificare una variabile – Cercare di modificare una const genera un errore del compilatore • puntatori const - puntano sempre alla stessa locazione di memoria – Devono essere inizializzati al momento della dichiarazione int *const myPtr = &x; • Tipo int *const - puntatore costante ad un dato int const int *myPtr = &x; • Puntatore ad un dato const int const int *const Ptr = &x; • puntatore const ad un dato const int 1 /* fig07_13.c 2 Attempting to modify a constant pointer to 3 non-constant data */ 4 5 #include <stdio.h> 6 7 int main() 8 { 9 int x, y; 10 11 è possibile cambiare *ptr - x non è una costante. int * const ptr = &x; /* ptr is a constant pointer to an 12 integer. An integer can be modified 13 through ptr, but ptr always points 14 to the same memory location. */ 15 *ptr = 7; 16 ptr = &y; 17 18 Modificare ptr è un errore ptr è un puntatore costante. return 0; 19 } FIG07_13.c: FIG07_13.c: Error Error E2024 E2024 FIG07_13.c FIG07_13.c 16: 16: Cannot Cannot modify modify aa const const object object in in function main function main *** *** 11 errors errors in in Compile Compile *** *** Espressioni con puntatori e l’aritmetica dei puntatori • Le operazioni aritmetiche possono essere eseguite anche con puntatori – Incremento/decremento di un puntatore (++ o --) – Aggiungere un intero ad un puntatore ( + o += , - o -=) – I puntatori possono essere sottratti uno dall’altro – Operazioni non significative, a meno che non siano eseguite su vettori …continua • Vettore int di 5 elementi su una macchina in cui gli interi sono rappresentati in 4 byte – vPtr punta al primo elemento, v[0] alla locazione 3000, (vPtr = 3000) – vPtr +=2; pone vPtr a 3008 • vPtr punta a v[2] (incrementato di 2, ma ogni intero occupa 4 byte) locazione 3000 3004 v[0] v[1] variabile puntatore vPtr 3008 v[2] 3012 v[3] 3016 v[4] …continua • Sottrazione di puntatori – Restituisce il numero di elementi fra i due puntatori vPtr2 = v[2]; vPtr = v[0]; v=vPtr2 - vPtr si ha v=2. • Confronto di puntatori ( <, == , > ) – Solo fra puntatori che si riferiscono allo stesso vettore – Determinare se un puntatore fa riferimento ad un elemento con indice più alto di quello puntato dall’altro puntatore – Determinare se un puntatore sia NULL …continua • Puntatori allo stesso tipo possono essere assegnati l’uno all’altro – Eccezione: puntatore a void (ovvero void *) • Puntatore generico, rappresenta qualsiasi tipo • Non è necessario l’operatore cast per convertire un puntatore a un puntatore void • Non è possibile risolvere il riferimento di un puntatore void Relazione tra puntatori e vettori • I vettori e i puntatori sono strettamente correlati – Il nome di un vettore è come un puntatore costante – I puntatori possono essere utilizzati nelle operazioni al posto degli indici di un vettore • Sia b[5] un vettore e bPtr un puntatore bPtr = b; Il nome del vettore è in realtà l’indirizzo del primo elemento oppure bPtr = &b[0]; che esplicitamente assegna a bPtr l’indirizzo del primo elemento …continua • L’elemento b[n] – può essere reperito con l’espressione *(bPtr + n ) – n è lo scostamento dal puntatore (offset) *bPtr+3 – Il nome stesso del vettore può usare l’aritmetica dei puntatori. b[3] è equivalente a *(b + 3) – Anche con i puntatori possono essere usati gli indici (notazione con puntatore/indice) bPtr[3] è equivalente b[3] Nota • È compito del programmatore sapere quando l’incremento di un puntatore ha significato. Altrimenti si rischia di accedere a zone di memoria estranee all’array con risultati anche disastrosi!!! Vettori di puntatori • I vettori possono contenere puntatori - vettori di stringhe char *suit[4] = {"Hearts","Diamonds","Clubs","Spades"}; – Stringa: puntatore al primo carattere – char * - ciascun elemento di suit è un puntatore a char – Le stringhe non sono contenute realmente nel vettore - nel vettore ci sono solo i puntatori alle stringhe suit[0] ’H’ ’e’ ’a’ ’r’ ’t’ ’s’ ’\0’ suit[1] ’D’ ’i’ ’a’ ’m’ ’o’ ’n’ ’d’ suit[2] ’C’ ’l’ ’u’ ’b’ ’s’ ’\0’ suit[3] ’S’ ’p’ ’a’ ’d’ ’e’ ’s’ ’s’ ’\0’ ’\0’ • il vettore suit ha dimensione fissa, ma le stringhe possono essere di dimensione qualsiasi (altrimenti matrice con dim fissata → SPRECO di MEMORIA) Studio di un caso: simulatore di un mescolatore e distributore di carte • Mescolatore di carte – Usa un vettore di puntatori a stringhe – Usa un vettore bidimensionale (seme, valore) 0 Hearts Diamonds Clubs Spades 1 2 3 4 5 6 7 8 9 10 11 0 1 2 3 deck[2][12] rappresenta il King di Clubs Clubs King – Nel vettore saranno inseriti i numeri 1-52 che rappresentano l’ordine in cui le carte saranno distribuite 12 …continua • Pseudocodice - livello Top: Mischiare e distribuire 52 carte Primo raffinamento Inizializzare i vettori dei semi, dei valori, la matrice delle carte Secondo raffinamento Per ciascuna delle 52 carte Inserire il numero di estrazione in un elemento libero della matrice delle carte scelto a caso Mischiare le carte Terzo raffinamento Scegliere a caso un elemento della matrice di carte Finchè l’elemento è già stato scelto precedentementte Scegliere a caso un elemento Inserire il numero di estrazione nell’elemento della matrice di carte Per ciascuna delle 52 carte Distribuire le 52 carte Trovare il numero di estrazione nella matrice delle carte e visualizzare il valore e il seme della carta Per ciascun elemento della matrice di carte Se l’elemento contiene il numero di estrazione Visualizzare il valore e il seme della carta 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 /* Card shuffling dealing program */ #include <stdio.h> #include <stdlib.h> #include <time.h> void shuffle( int [][ 13 ] ); void deal( const int [][ 13 ], const char *[], const char *[] ); int main() { const char *suit[ 4 ] = { "Hearts", "Diamonds", "Clubs", "Spades" }; const char *face[ 13 ] = { "Ace", "Deuce", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King" }; int deck[ 4 ][ 13 ] = { 0 }; srand( time( 0 ) ); shuffle( deck ); deal( deck, face, suit ); return 0; } void shuffle( int wDeck[][ 13 ] ) { int row, column, card; for ( card = 1; card <= 52; card++ ) { 33 do { 34 row = rand() % 4; 35 column = rand() % 13; 36 I numeri 1-52 sono messi a caso nel vettore deck . } while( wDeck[ row ][ column ] != 0 ); 37 38 39 wDeck[ row ][ column ] = card; } 40 } 41 42 void deal( const int wDeck[][ 13 ], const char *wFace[], 43 const char *wSuit[] ) 44 { 45 int card, row, column; 46 47 for ( card = 1; card <= 52; card++ ) 48 49 for ( row = 0; row <= 3; row++ ) 50 51 for ( column = 0; column <= 12; column++ ) 52 53 if ( wDeck[ row ][ column ] == card ) 54 printf( "%5s of %-8s%c", 55 wFace[ column ], wSuit[ row ], 56 card % 2 == 0 ? '\n' : '\t' ); 57 } Ricerca in deck il numero card , poi visualizza face e suit. Esecuzione Esecuzione Six Six Ace Ace Ace Ace Queen Queen Ten Ten Ten Ten Ten Ten Four Four Six Six Eight Eight Nine Nine Deuce Deuce Five Five Deuce Deuce Five Five King King Deuce Deuce Ace Ace Three Three Nine Nine Four Four Eight Eight Jack Jack Five Five Four Four Jack Jack of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of Clubs Clubs Spades Spades Hearts Hearts Clubs Clubs Hearts Hearts Spades Spades Diamonds Diamonds Diamonds Diamonds Diamonds Diamonds Hearts Hearts Hearts Hearts Spades Spades Clubs Clubs Diamonds Diamonds Spades Spades Diamonds Diamonds Hearts Hearts Clubs Clubs Clubs Clubs Clubs Clubs Hearts Hearts Diamonds Diamonds Diamonds Diamonds Hearts Hearts Clubs Clubs Clubs Clubs Seven Seven Ace Ace Queen Queen Seven Seven Deuce Deuce Three Three Four Four Ten Ten Six Six Three Three Three Three Six Six Eight Eight Eight Eight King King Jack Jack Queen Queen King King King King Nine Nine Queen Queen Nine Nine Seven Seven Five Five Jack Jack Seven Seven of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of of Diamonds Diamonds Diamonds Diamonds Diamonds Diamonds Hearts Hearts Clubs Clubs Spades Spades Spades Spades Clubs Clubs Spades Spades Diamonds Diamonds Hearts Hearts Hearts Hearts Clubs Clubs Spades Spades Clubs Clubs Spades Spades Hearts Hearts Spades Spades Hearts Hearts Spades Spades Spades Spades Diamonds Diamonds Clubs Clubs Diamonds Diamonds Hearts Hearts Spades Spades Puntatori a funzioni • Puntatore a una funzione – Contiene il suo indirizzo in memoria – Analogamente al nome di un vettore, che è l’indirizzo del primo elemento, il nome di una funzione è in realtà l’indirizzo di memoria dal quale inizia il codice che definisce la funzione stessa • I puntatori a funzioni possono essere – passati e restituiti dalle funzioni – memorizzati in vettori – assegnati ad altri puntatori a funzioni …continua • Esempio: bubblesort – La funzione bubble riceve come argomento un puntatore a una funzione ausiliaria (ascending o descending) • bubble chiama una delle funzioni ausiliarie • così si può scegliere fra ordinamento ascendente o discendente – Nell’intestazione della funzione bubble compare il parametro: int ( *compare )( int, int ) • questo avverte bubble che riceverà come parametro un puntatore a una funzione che accetta due argomenti interi e che restituisce un risultato dello stesso tipo – Se non avessimo incluso le parentesi, al dichiarazione sarebbe stata: int *compare( int, int ) • che dichiara una funzione che riceve due interi e restituisce un puntatore dello stesso tipo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 /* Multipurpose sorting program using function pointers */ #include <stdio.h> #define SIZE 10 void bubble( int [], const int, int (*)( int, int ) ); int ascending( int, int ); int descending( int, int ); Si noti il parametro puntatore a funzione int main() { int order, counter, a[ SIZE ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 }; printf( "Enter 1 to sort in ascending order,\n" "Enter 2 to sort in descending order: " ); scanf( "%d", &order ); printf( "\nData items in original order\n" ); for ( counter = 0; counter < SIZE; counter++ ) printf( "%5d", a[ counter ] ); if ( order bubble( printf( } else { bubble( printf( } == 1 ) { a, SIZE, ascending ); "\nData items in ascending order\n" ); a, SIZE, descending ); "\nData items in descending order\n" ); 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 for ( counter = 0; counter < SIZE; counter++ ) printf( "%5d", a[ counter ] ); printf( "\n" ); return 0; } void bubble( int work[], const int size, int (*compare)( int, int ) ) { int pass, count; ascending e descending restituiscono true o false. bubble chiama swap se la funzione restituisce true. void swap( int *, int * ); for ( pass = 1; pass < size; pass++ ) for ( count = 0; count < size - 1; count++ ) if ( (*compare)( work[ count ], work[ count + 1 ] ) ) swap( &work[ count ], &work[ count + 1 ] ); } void swap( int *element1Ptr, int *element2Ptr ) { int temp; temp = *element1Ptr; *element1Ptr = *element2Ptr; *element2Ptr = temp; } Si noti come i puntatori a funzioni siano chiamati usando l’operatore di deriferimento. L’operatore * non è necessario, ma evidenzia che compare è un puntatore a funzione e non una funzione. 65 int ascending( int a, int b ) 66 { 67 return b < a; /* swap if b is less than a */ 68 } 69 70 int descending( int a, int b ) 71 { 72 return b > a; /* swap if b is greater than a */ 73 } Enter Enter 11 to to sort sort in in ascending ascending order, order, Enter Enter 22 to to sort sort in in descending descending order: order: 11 Data Data items items in in original original order order 22 66 44 88 10 12 10 12 89 89 68 68 Data items in ascending order Data items in ascending order 22 44 66 88 10 10 12 12 37 37 45 45 45 45 37 37 68 68 89 89 Enter Enter 11 to to sort sort in in ascending ascending order, order, Enter Enter 22 to to sort sort in in descending descending order: order: 22 Data Data items items in in 22 66 44 Data Data items items in in 89 89 68 68 45 45 original original order order 88 10 12 10 12 89 89 68 68 descending order descending order 37 88 66 37 12 12 10 10 45 45 37 37 44 22 Array come parametro di funzione • • In realtà viene passato il puntatore all’array (che è un singolo valore numerico) Esistono due modi per passare un array ad una funzione: 1. Si passa l’array come primo parametro e la dimensione dell’array come secondo parametro 2. Si passa il puntatore all’array • (parametro.cpp) • Le modifiche apportate dalle funzioni sono permanenti { int v[4]={1,2,3,4}; { int v[4]={1,2,3,4}; Funz(v,4); Funz(&v); } } int Funz(int w, int dim) int Funz(int *w) Passaggio di parametri per indirizzo • Passa per valore l’indirizzo della variabile che si vuole leggere o modificare • Rende nota la locazione della variabile alla funzione • Quindi modificabile dalle istruzioni della funzione Bubble Sort usando la chiamata per riferimento • Realizziamo bubblesort usando puntatori – Scambiare due elementi – la funzione swap deve ricevere gli indirizzi degli elementi del vettore (usando &) • Gli elementi di un vettore hanno per default la chiamata per valore – Usando i puntatori e l’operatote *, swap può scambiare gli elementi del vettore • Psuedocodice Inizializzare il vettore visualizzare i dati nell’ordine originale Chiamare la funzione bubblesort visualizzare il vettore ordinato Definire bubblesort …continua • sizeof – Restituisce la dimensione in byte dell’operando – Per i vettori: dimensione di 1 elemento * numero di elementi – se sizeof(int) = 4 bytes, allora int myArray[10]; printf( "%d", sizeof( myArray ) ); visualizzerà 40 • sizeof può essere usato con – Nomi di variabili – Nomi di tipo – Valori costanti 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 /* This program puts values into an array, sorts the values into ascending order, and prints the resulting array. */ #include <stdio.h> #define SIZE 10 void bubbleSort( int *, const int ); int main() { int a[ SIZE ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 }; int i; printf( "Data items in original order\n" ); for ( i = 0; i < SIZE; i++ ) printf( "%4d", a[ i ] ); bubbleSort( a, SIZE ); /* sort the array */ printf( "\nData items in ascending order\n" ); for ( i = 0; i < SIZE; i++ ) printf( "%4d", a[ i ] ); printf( "\n" ); return 0; } void bubbleSort( int *array, const int size ) { void swap( int *, int * ); 33 int pass, j; 34 for ( pass = 0; pass < size - 1; pass++ ) 35 36 for ( j = 0; j < size - 1; j++ ) 37 38 if ( array[ j ] > array[ j + 1 ] ) 39 swap( &array[ j ], &array[ j + 1 ] ); 40 } 41 42 void swap( int *element1Ptr, int *element2Ptr ) 43 { 44 int hold = *element1Ptr; 45 *element1Ptr = *element2Ptr; 46 *element2Ptr = hold; 47 } Esecuzione Esecuzione Data Data 22 Data Data 22 items items 66 items items 44 in in 44 in in 66 original original order order 88 10 10 12 12 89 89 68 68 ascending order ascending order 88 10 10 12 12 37 37 45 45 45 45 37 37 Introduzione al C Le stringhe Array di caratteri •Non esiste in C un tipo di dato predefinito per le stringhe •Le stringhe sono rappresentate come array di caratteri. •Questo approccio alle stringhe consente di avere una potenza e flessibilità maggiori rispetto ai linguaggi che utilizzano un tipo speciale per le stringhe. 44 Stringhe •Una stringa è un array di caratteri che finisce con un terminatore NULL. •Un NULL viene specificato come il carattere speciale ‘\0’ e corrisponde nella codifica ASCII al carattere che ha valore zero. Pertanto, •se ad esempio vogliamo dichiarare un array stringa che contenga la stringa di caratteri “ciao”: char stringa[5]; (Questo per lasciare spazio al terminature NULL alla fine della stringa). 45 …continua Una costante stringa è una lista di caratteri racchiusi fra doppi apici: “ciao” ciao” è una costante stringa “” prende il nome di stringa nulla perchè contiene soltanto il terminatore NULL e nessun altro carattere. Nelle costanti stringa non è necessario aggiungere manualmente il terminatore NULL alla fine della stringa, il compilatore lo fa automaticamente, perciò la stringa “ciao” in memoria apparirà così: c i a o ‘\0’ 46 Inizializzazione di un array di caratteri Gli array di caratteri permettono di utilizzare un’inizializzazione abbreviata che assume questa forma: char nome_array[dimensione]= nome_array[dimensione]=“ stringa”; ]=“stringa” char str[5]=“ str[5]=“ciao” ciao”; Al posto della forma classica per gli array interi. tipo nome_variabile nome_variabile[]={e1, []={e1,… []={e1,…,en}; char str[5]={‘ str[5]={‘c’,’i’,’a’,’o’,’\0’}; 47 Array come parametro di funzione • Che cosa stampa? #include <stdio.h <stdio.h> stdio.h> void func(char a[]); int main() { char a[]= “12345” 12345”; func(a); func(a); return 0; } void func(char a[]) { printf(“ printf(“%u\ %u\n”, sizeof a); } 48 #include <stdio.h <stdio.h> stdio.h> /*corretto*/ void func( func(char a[], size_t s) { printf("%u printf("%u\ ("%u\n", s); } int main() main() { char a[]="12345"; func(a, func(a, sizeof a); return 0; 49 } 50 Lettura di una stringa da tastiera: gets() • char *gets( *gets(char *str); *str); • Legge i caratteri da stdio finché non incontra \n o EOF • \n è rimpiazzato da \0 • Restituisce – un puntatore alla stringa – il puntatore nullo se si è verificato qualche errore oppure se ha incontrato \n o EOF prima che sia stato inserito qualche carattere • Il programmatore deve allocare spazio di memoria sufficiente a contenere l’input per prevenire errori di sovrascrittura! (input_ge.cpp)51 fgets() È simile a gets() ma più flessibile perchè permette di specificare 1)l’input stream e 2)il numero massimo di caratteri da inserire Questa funzione prende in input tre argomenti: char *fgets(char *fgets(char *str,int n, FILE *fp *fp); fp); legge al massimo n-1 caratteri dallo standard input (la tastiera) la lettura da tastiera termina quando viene incontrato un carattere \n o EOF in input oppure n-1 caratteri, ma l’esecuzione non ritorna da fgets() finchè non è premuto INVIO viene aggiunto il terminatore NULL alla fine della 52 stringa (vedi stringa2.c) Lettura di una stringa da tastiera Utilizzo della funzione scanf(). Problemi: scanf() smette di leggere una stringa non appena incontra un carattere di spaziatura (space, tab o newline), ma restituisce l’input solo dopo l’invio. scanf() non vieta di inserire una stringa di caratteri più lunga delle dimensioni dell’array che la può contenere. Ritorna il numero di immissioni effettuate con successo. 53 #include <stdio.h <stdio.h> stdio.h> int main() main() { char s[80]; int iNum; iNum; int count; count; puts("Inserire puts("Inserire una stringa di caratteri e poi un intero \ decimale\ decimale\n"); count=scanf("%s count=scanf("%s\ ("%s\t %d",s,&iNum %d",s,&iNum); &iNum); printf("Il printf("Il numero di valori inseriti e' %d\ %d\n",count n",count); count); printf("La printf("La stringa inserita e' \"%s\ "%s\"\n",s); printf("Il printf("Il numero inserito e' %d\ %d\n",iNum n",iNum); iNum); } 54 Si può specificare con %ns il numero massimo di caratteri accettati Ma, i caratteri extra rimangono nel buffer di IO • Esempi: – Ancora sull’inserimento di stringhe di caratteri con scanf() scanf() : input_sc.cpp – Come eliminare i caratteri extra dal buffer: extra_ca.cpp 55 Quiz • Quando il compilatore incontra un singolo carattere tra apici singoli, come viene interpretato? • Per memorizzare una stringa di n caratteri quanti elementi deve avere l’array? • Quanti bytes sono allocati per memorizzare le seguenti variabili? – – – – char *str1={ “String 1” }; char *str2[]={ “String 2” }; char string3; char str4[20]={ “String 4” }; 56 • Usando la dichiarazione: • char *string=“ *string=“A string! string!”; • che valore hanno le seguenti: – string[0] – *string – string[9] – string[33] – string+8 – string 57