• Save
Array in C++
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Array in C++

on

  • 465 views

Introduzione agli Array in C++. Argomenti trattati: decadimento a puntatore di un array; conseguenze del meccanismo di decadimento sul passaggio di array a funzioni; array multidimensionali e il ...

Introduzione agli Array in C++. Argomenti trattati: decadimento a puntatore di un array; conseguenze del meccanismo di decadimento sul passaggio di array a funzioni; array multidimensionali e il concetto di puntatore ad array; come passare array multidimensionali a funzioni; gli iteratori come generalizzazione di un puntatore ad elemento di un array ed una breve scorsa di come usare gli iteratori con gli algoritmi standard (e.g., std::copy) del C++.

Statistics

Views

Total Views
465
Views on SlideShare
465
Embed Views
0

Actions

Likes
1
Downloads
1
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Array in C++ Presentation Transcript

  • 1. Array Ilio Catallo – Politecnico di Milano ilio.catallo@polimi.it
  • 2. Outline ¤  Introduzione agli array ¤  Array e puntatori ¤  Array e funzioni ¤  Array multidimensionali ¤  Array e iteratori
  • 3. Introduzione agli array
  • 4. Che cos’è un array? Un array è un contenitore di oggetti di un singolo tipo dato ¤  Un array si definisce specificandone: ¤  Il tipo dato comune ad ogni cella ¤  Il nome dell’array ¤  La sua dimensione int numbers[5]; // an array of five int’s named ‘numbers’
  • 5. Rappresentazione in memoria numbers: ¤  Il compilatore riserva il quantitativo di memoria necessaria per accomodare 5 elementi di tipo int! ¤  Le celle riservate sono contigue ¤  Ogni oggetto nell’array numbers è associato ad un indice, che permette di accedere all’oggetto ¤  L’indice 0 è associato al primo elemento, l’indice 4 all’ultimo
  • 6. Assegnamento ed inizializzazione ¤  Cosa è possibile fare: ¤  Inizializzare un array con una lista di inizializzazione ¤  Cosa non è possibile fare: ¤  Inizializzare un array come una copia di un’altro array ¤  Assegnare un array int numbers[] = {1, 7, 13}; // ok, initialization list int other_numbers[] = numbers // error, copy initialization int more_numbers[3]; more_numbers[] = {1, 7, 13} // error, assignment
  • 7. Array non inizializzati ¤  Se in un una funzione si definisce un array senza inizializzarlo, il valore iniziale degli elementi dipende dal loro tipo T ¤  Se T è un tipo predefinito, gli elementi sono inizializzati a default (default initialization) ¤  Altrimenti, si usa il costruttore di default di T per inizializzare tutti gli elementi ¤  Se T non prevede un costruttore di default, il programma non compila
  • 8. Accedere agli elementi di un array ¤  Per accedere all’i-esimo elemento nell’array si utilizza l’operatore [] (indexing operator*) int numbers[] = {1, 7, 13}; std::cout << "first element: ” << numbers[0] << std::endl; *anche detto subscript operator
  • 9. Accessi fuori dall’intervallo ¤  Accessi fuori dall’intervallo ammissibile di un array sono considerati undefined behavior ¤  Il programma compila, ma non è possibile prevedere cosa accadrà dopo aver effettuato l’accesso errato int numbers[3]; std::cout << numbers[100]; // undefined behavior ¤  A differenza di altri linguaggio, il C++ non segnala in fase d’esecuzione questo tipo di errore
  • 10. Tipo dato degli elementi di un array ¤  Ogni elemento dell’array è del tipo specificato al momento della definizione dell’array int numbers[] = {1, 7, 13}; int x = numbers[2]; // initialize the int variable // ‘x’ with another int // variable (numbers[2]) ¤  È possibile manipolare i singoli elementi di numbers come una qualsiasi altra variabile di tipo int!
  • 11. Tipo dato di un array (1) ¤  Quando definiamo un array, specifichiamo il tipo dato dei singoli elementi: int numbers[5]; // an array of five int’s named ‘numbers’ ¤  int non denota il tipo dato di numbers, bensì il tipo dato dei suoi elementi numbers[0]…numbers[4]! ¤  Eppure numbers è una variabile, e come tale deve avere un tipo dato Qual è il tipo dato di un array?
  • 12. Tipo dato di un array (2) Per un tipo dato T, T[m] è il tipo dato array di m elementi di tipo T ¤  Il tipo array è un tipo dato composto, in quanto dipende da due fattori distinti: ¤  Il tipo dato T degli elementi ¤  Il numero di elementi m! int numbers[5]; // an array of five int’s named ‘numbers’ ¤  Il tipo dato di numbers è quindi int[5], cioè “array di 5 elementi di tipo int”
  • 13. Importanza del tipo composto ¤  È sufficiente che una sola delle due quantità (tipo T, dimensione m) cambi per cambiare il tipo dato dell’array int numbers[5]; char letters[5]; int other_numbers[10]; int other_letters[10]; // // // // numbers is of letters is of other_numbers other_letters type int[5] type char[5] is of type int[10] is of type char[10] ¤  numbers e other_numbers non sono variabili dello stesso tipo: ¤  numbers è di tipo int[5]! ¤  other_numbers è di tipo int[10]!
  • 14. Dimensioni di un array (1) ¤  La dimensione m è parte del tipo dato dell’array e deve quindi essere nota a tempo di compilazione (compiletime) ¤  Le uniche quantità che il compilatore può conoscere prima che il codice venga eseguito sono le espressioni costanti int numbers[5]; 5 è un letterale ed è quindi un’espressione costante, perchè noto a compile-time
  • 15. Dimensioni di un array (2) ¤  Un’espressione costante può essere memorizzata in una variabile costante, usando: ¤  il qualifier const ¤  lo specifier constexpr (dal C++11) ¤  Il tipo dato usato per mantenere la dimensione di un array è size_t! ¤  size_t è un tipo intero senza segno in grado di memorizzare la dimensione del più grande array allocabile constexpr size_t DIM = 5; // C++11 size_t const DIM = 5; // C++03
  • 16. Array e puntatori
  • 17. Puntatore al primo elemento (1) ¤  Supponiamo di voler memorizzare in un puntatore l’indirizzo del primo elemento di un array int numbers[] = {1, 7, 13, 5, 9}; ¤  numbers[0] è un oggetto di tipo int ed ha un indirizzo, possiamo memorizzare tale indirizzo in un puntatore a int! int* first_element_ptr = &numbers[0];
  • 18. Puntatore al primo elemento (2) numbers: 1 first_elem_ptr: 7 &numbers[0]! 13 int* 5 9 int[5]
  • 19. Puntatore al primo elemento (3) ¤  È possibile ottenere il l’indirizzo del primo elemento di un array utilizzando un’espressione compatta ¤  Dato un array, ad esempio: int numbers[] = {1, 7, 13, 5, 9}; ¤  Le seguenti due espressioni sono equivalenti: int* first_element_ptr = &numbers[0]; Indirizzo del primo elemento int* first_element_ptr = numbers; Nome dell’array
  • 20. Decadimento a puntatore (1) int* first_element_ptr = numbers; ¤  Come può mai funzionare questa cosa? ¤  first_element_ptr è di tipo int*! ¤  numbers è di tipo int[5]! ¤  Il tipo int[5] viene implicitamente convertito a int*! ¤  Il risultato di tale conversione è un puntatore al primo elemento dell’array ¤  Questo fenomeno prende il nome di array-to-pointer decay
  • 21. Decadimento a puntatore (2) ¤  La conversione non trasforma l’array in un puntatore ¤  Le variabili non cambiano tipo, numbers sarà sempre un int[5]! int* first_element_ptr = numbers; ¤  Cosa accade? ¤  Viene creato un puntatore temporaneo di tipo int*, risultato del decadimento di numbers! ¤  Il contenuto di tale puntatore (cioè l’indirizzo di numbers[0]) viene copiato in first_element_ptr! ¤  Al termine dell’istruzione, il puntatore temporaneo viene distrutto
  • 22. Decadimento a puntatore (3) ¤  Nel valutare un’espressione che coinvolge un array di tipo T[m], il compilatore: ¤  Se l’espressione è corretta, mantiene il tipo T[m]! ¤  In caso contrario, converte T[m] a T*! int numbers[] = {1, 7, 13, 5, 9}; size_t numbers_size = sizeof(numbers); // numbers is // treated as a // int[5] int* ptr_to_1st_element = numbers; // numbers is converted // to int*
  • 23. Perdita di informazione ¤  Il fenomeno si chiama decadimento perchè viene persa dell’informazione ¤  Una volta convertito in puntatore, non è più possibile conoscere la dimensione dell’array ¤  Ho solo un puntatore al primo elemento, ma quanti elementi sono presenti nell’array? ¤  In altre parole, l’unico punto in comune tra i tipi dato T[m] e T[n] è che entrambi decadono a T*! ¤  I rispettivi decadimenti condividono lo stesso tipo dato T*!
  • 24. Aritmetica dei puntatori (1) ¤  L’operatore [] permette di accedere e manipolare gli elementi di un array ¤  Un secondo modo di interagire con gli elementi di un array è utilizzare l’aritmetica dei puntatori ¤  L’aritmetica dei puntatori si compone di un insieme di operazione (aritmetiche) sui puntatori, in particolare: ¤  ¤  ¤  ¤  Incremento e decremento Addizione e sottrazione Confronto Assegnamento
  • 25. Aritmetica dei puntatori (2) ¤  Dato un puntatore ptr al primo elemento di un array, l’espressione ptr + i restituisce un puntatore all’i-esimo elemento dell’array int numbers[] = {1, 7, 13, 5, 9}; int* first_element_ptr = numbers; int* third_element_ptr = first_element_ptr + 2; int* fifth_element_ptr = first_element_ptr + 4; std::cout << "the third element is " << *third_element_ptr; third_element_ptr è un puntatore, per ottenere il valore puntatato serve l’operatore di indirezione *!
  • 26. Aritmetica dei puntatori (3) numbers: first_el_ptr: 1 7 13 third_el_ptr: 9 fifth_el_ptr: = = first_el_ptr + 2 5 first_el_ptr + 4 int[5] int*
  • 27. Aritmetica dei puntatori (4) ¤  Grazie al decadimento è possibile evitare l’uso di variabili d’appoggio (come first_element_ptr): int numbers[] = {1, 7, 13, 5, 9}; std::cout << "the third element is " << *(numbers + 2); std::cout << "the fifth element is " << *(numbers + 4); ¤  Le parentesi sono importanti ¤  *numbers + 4 è equivalente a numbers[0] + 4! ¤  *(numbers + 4) è equivalente a numbers[4]!
  • 28. Indexing operator vs. aritmetica dei puntatori (1) ¤  L’operatore [] è definito in termini di aritmetica dei puntatori Dato un array a ed un indice i, l'operazione a[i] è implementata come *(a + i)! ¤  L’operatore [] è dunque una scrittura sintetica per effettuare una somma su puntatore
  • 29. Indexing operator vs. aritmetica dei puntatori (2) ¤  Di conseguenza… ¤  L’operatore [] è in realtà un operatore definito sui puntatori (si può usare sugli array grazie al decadimento) int* first_elem_ptr = numbers;
 std::cout << first_elem_ptr[3]; // implemented as " // *(first_elem_ptr + 3)" ¤  Le due seguenti scritture sono equivalenti (la somma è commutativa) std::cout << numbers[2]; // implemented as *(numbers + 2) std::cout << 2[numbers]; // implemented as *(2 + numbers)
  • 30. Gli array non sono puntatori ¤  RICORDA: Gli array non sono puntatori ¤  Sono due tipi dati distinti, anche se strettamente legati ¤  Per convincersene è sufficiente notare che la loro rappresentazione in memoria è diversa 0 m-1 a: ptr: … T* T[m]
  • 31. Array e funzioni
  • 32. La funzione sum_int_array! ¤  Vogliamo scrivere una funzione sum_int_array che restituisca la somma dei valori contenuti in un array di interi int main() { int numbers[] = {1, 7, 13, 5, 9}; int sum = sum_int_array(numbers); std::cout << ”the elements sum up to " << sum << std::endl; } Viene stampato 35!
  • 33. Passare array a funzioni ¤  Non è possibile passare ad una funzione un array per copia ¤  Non è possibile inizializzare l’array della funzione chiamata copiando il contenuto dell’array della funzione chiamante
  • 34. Passaggio per indirizzo (1) ¤  Nel passaggio per indirizzo viene fornita in ingresso alla funzione chiamata una copia dell’indirizzo del parametro che si vuole passare ¤  IDEA: fare in modo che sum_int_array riceva in ingresso: ¤  Un puntatore alla prima cella dell’array ¤  La dimensione dell’array int sum_int_array(int* array, size_t dim_array);"
  • 35. Passaggio per indirizzo (2) ¤  La funzione chiamante fornisce l’indirizzo del primo elemento dell’array e la dimensione dell’array ¤  Grazie al decadimento, è possibile usare il nome dell’array per ottenere l’indirizzo del primo elemento int sum_int_array(int* array, size_t dim_array);" int main() { int numbers[] = {1, 7, 13, 5, 9}; int sum = sum_int_array(numbers, 5); } Il 1° parametro in ingresso a sum_int_array è un puntatore, quindi l’array numbers decade a int*
  • 36. Passaggio per indirizzo (3) ¤  L’implementazione di sum_int_array è dunque int sum_int_array(int* array, size_t dim_array) { int sum = 0; for (size_t i = 0; i < dim_array; ++i) sum += array[i]; return sum; } ¤  L’operatore [] ci permette di manipolare il puntatore array con la stessa sintassi che useremmo per un vero array
  • 37. Sintassi alternativa (1) ¤  Esiste una sintassi alternativa per acquisire l’indirizzo del primo elemento di un array ¤  I tre seguenti prototipi di sum_int_array sono equivalenti: int sum_int_array(int* array, size_t dim_array);" int sum_int_array(int array[], size_t dim_array);" int sum_int_array(int array[5], size_t dim_array);" ¤  NOTA: tale equivalenza vale unicamente quando si dichiara una funzione
  • 38. Sintassi alternativa (2) int sum_int_array(int array[], size_t dim_array);" int sum_int_array(int array[5], size_t dim_array);" ¤  L’utilizzo di uno dei due prototipi alternativi non significa che l’array verrà passato per copia ¤  la variabile array è di tipo int*, a prescindere da quale scrittura si utilizzi ¤  I prototipi alternativi danno l’illusione di agire su degli array ¤  Sono una delle cause della confusione tra array e puntatori
  • 39. Array multidimensionali
  • 40. Array multidimensionali (1) ¤  Gli array multidimensionali permettono estendere il concetto di array a più di una dimensione 1 7 14 8 6 12 27 32 5
  • 41. Array multidimensionali (2) ¤  Nei normali array è sufficiente un solo indice per identificare un elemento dell’array ¤  Negli array multidimensionali sono necessari tanti indici quante dimensioni 1 indice di riga i! 7 14 8 6 12 27 32 5 indice di colonna j!
  • 42. Array di array (1) ¤  In C++ non esistono array multidimensionali ¤  Gli array multidimensionali vengono realizzati mediante array di array, cioè array i cui elementi sono a loro volta array ¤  Come per ogni array si definisce il numero di elementi ¤  Ogni elemento è però a sua volta un array ¤  Bisogna specificare una seconda quantità: il numero di elementi in ogni sotto-array int matrix[4][3]; // matrix is an array of 4 elements; " // each element is an array of 3 " // elements of type int"
  • 43. Array di array (2) 1 7 14 1 14 8 6 12 27 ! 7 32 5 Array multidimensionale! 8 6 12 27 32 5 Array di array!
  • 44. Array di array (3) ¤  Un array multidimensionale è un array monodimensionale i cui elementi sono a loro volta array ¤  È la dimensione più interna che determina il numero di sottoarray ¤  In altre parole, la dicitura: int matrix[4][3]; ¤  Può essere pensata come: int[3] matrix[4]; // non-working code, // just for the sake of explanation
  • 45. Rappresentazione in memoria (1) ¤  Abbiamo introdotto due rappresentazioni per gli array multidimensionali: 1 7 14 1 14 8 6 12 27 ! 7 32 5 8 6 12 27 32 5 ¤  Tali rappresentazioni sono intuitive, ma non riflettono come un array multidimensionale è realmente memorizzato in memoria
  • 46. Rappresentazione in memoria (2) ¤  Sappiamo che la memoria è modellata come una sequenza di celle di memoria ¤  Gli array multidimensionale sono dunque memorizzati come una sequenza di celle contigue matrix:" 1 7 14 matrix[0] 8 16 12 matrix[1] 27 32 5 matrix[2]
  • 47. Inizializzazione ¤  Gli array multidimensionali possono essere inizializzati mediante una lista di inizializzazione int matrix[3][3] = {{1, 7, 14}, " {8, 16, 12}, " {27, 32, 5}};" ¤  Le parentesi {} demarcano l’inizio e la fine di ogni riga ¤  Una scrittura equivalente (anche se meno leggibile): int matrix[3][3] = {1, 7, 14, 8, 16, 12, 27, 32, 5};"
  • 48. Accedere agli elementi ¤  Per accedere all’(i,j)-esimo elemento si utilizza una sequenza di operatori []! int matrix[3][3] = {{1, 7, 14}, " {8, 16, 12}, " {27, 32, 5}};" " std::cout << "element (2,2): " << matrix[2][2];"
  • 49. Puntatore al primo elemento (1) ¤  Supponiamo di voler memorizzare in un puntatore l’indirizzo del primo elemento di un array multidim. int matrix[3][3] = {{1, 7, 14}, " {8, 16, 12}, " {27, 32, 5}};" ¤  matrix è un array di righe ¤  matrix[0] è di tipo int[3] (prima riga della matrice) ¤  Per salvarne l’indirizzo, è necessario un puntatore a int[3]! ¤  Come si definisce un puntatore a tipo T[m]?
  • 50. Puntatore ad array (1) ¤  La sintassi per dichiarare un puntatore ad array è leggermente diversa da quella usata fino ad adesso: int numbers[] = {1, 7, 13, 5, 9}; int (*numbers_ptr)[5] = &numbers; // numbers_ptr points to // numbers ¤  Le parentesi sono importanti int* array_of_pointers[5] // // int (*pointer_to_array)[5] // // array of 5 elements of type pointers to int pointer to an array of 5 elements of type int
  • 51. Puntatore ad array (2) numbers: ptr_to_numbers: 1 7 &numbers! 13 int(*)[5] 5 9 int[5]
  • 52. Puntatore al primo elemento (2) ¤  Possiamo ottenere un puntatore al primo elemento di un array multidimensionale come: int matrix[3][3] = {{1, 7, 14}, " {8, 16, 12}, " {27, 32, 5}};
 " int (*ptr_to_first_row)[3] = &matrix[0];" ¤  Ovviamente possiamo usare il decadimento: int (*ptr_to_first_row)[3] = matrix;"
  • 53. Puntatore al primo elemento (3) matrix:! 27 ! 32 5 ! 1 7 14 ! 27 32 5 ! ptr_to_first_row:! &matrix[0]! int(*)[3]! int[3][3]!
  • 54. Alias per i sotto-array ¤  L’uso di alias per i tipi dato può aiutarci a rendere più leggibile il codice: int matrix[3][3] = {{1, 7, 14}, " {8, 16, 12}, " {27, 32, 5}};
 " typedef int matrix_row[3];
 matrix_row* ptr_to_first_row = matrix;" ¤  matrix_row è un altro nome per il tipo int[3] ¤  Un puntatore a matrix_row è la stessa cosa di un puntatore a int[3]!
  • 55. Aritmetica dei puntatori (1) ¤  L’aritmetica dei puntatori è definita anche su array multidim. int matrix[3][3] = {{1, 7, 14}, " {8, 16, 12}, " {27, 32, 5}};
 " typedef int matrix_row[3];
 matrix_row* ptr_to_first_row = matrix;" " matrix_row* ptr_to_second_row = ptr_to_first_row + 1;
 matrix_row* ptr_to_third_row = ptr_to_first_row + 2;" ¤  matrix è un array di righe ¤  Ogni elemento dell’array matrix è una riga della matrice ¤  Spostarsi di un elemento vuol dire passare alla riga successiva
  • 56. Aritmetica dei puntatori (2) matrix:! 27 ! 32 5 ! 1 7 14 ! 27 32 5 int[3][3]! ! int(*)[3]! ptr_to_first_row! ptr_to_second_row! ptr_to_third_row! = = ptr_to_first_row + 1! ptr_to_first_row + 2!
  • 57. Aritmetica dei puntatori (3) ¤  Dato un puntatore ad una riga dell’array multidim. ¤  Si dereferenzia il puntatore per ottenere la riga ¤  Si utilizza l’aritmetica dei puntatori per per ottenere un particolare elemento della riga int matrix[3][3] = {{1, 7, 14}, " {8, 16, 12}, " {27, 32, 5}};
 " typedef int matrix_row[3];
 matrix_row* ptr_to_third_row = matrix + 2;" " std::cout << "element (2,2): " << *(*ptr_to_third_row + 2);" terza riga dell’array multidim. terzo elemento della terza riga
  • 58. Aritmetica dei puntatori (4) ¤  Grazie al decadimento, possiamo combinare le due operazioni aritmetiche per ottenere un elemento dell’array multidimensionale int matrix[3][3] = {{1, 7, 14}, {8, 16, 12}, {27, 32, 5}}; typedef int matrix_row[3]; matrix_row* ptr_to_third_row = matrix + 3; std::cout << "element (2,2): " << *(*(matrix + 2) + 2); terza riga dell’array multidim. terzo elemento della terza riga
  • 59. Indexing operator vs. aritmetica dei puntatori ¤  Il comportamento dell’operatore [] rimane invariato Dato un array multdimensionale a e due indici i e j, l'operazione a[i][j] è implementata come *(*(a + i) + j)! ¤  Non è una nuova definizione dell’operatore, le due operazioni vengono solo eseguite in cascata ¤  Si può pensare ad a[i][j] come (a[i])[j]: ¤  Si estrae la riga i-esima ¤  Da questa si seleziona il j-esimo elemento
  • 60. Passare array multidimensionali a funzioni ¤  Supponiamo di voler scrivere una funzione sum_int_matrix che sommi i valori di un array multidim. di interi int main () { int matrix[3][3] = {{1, 7, 14}, {8, 16, 12}, {27, 32, 5}}; int sum = sum_int_matrix(matrix); std::cout << "the elements sum up to " << sum << std::endl; }
  • 61. Passaggio per indirizzo (1) ¤  IDEA: fare in modo che sum_int_matrix accetti un puntatore alla prima riga ¤  Un puntatore alla prima riga permette di: ¤  Muoversi tra gli elementi della stessa riga ¤  Muoversi tra righe successive int sum_int_matrix(int (*matrix)[3], size_t row_num); Puntatore ad un array di 3 elementi di tipo int, cioè un puntatore ad un riga dell’array multidimensionale!
  • 62. Passaggio per indirizzo (2) ¤  Quando si passano array multidim. tutte le dimensioni tranne la prima devono essere note a compile-time ¤  L’accesso ad un array multidim. di tipo T[m][n] avviene mediante un puntatore a tipo T[n]! ¤  L’uso del puntatore ci permette di non conoscere m, ma il valore n deve comunque essere noto int sum_int_array(int* array, size_t dim_array);" In caso di array nessuna dimensione è nota compiletime! int sum_int_matrix(int (*matrix)[3], size_t row_num); Il numero di elementi in ogni riga deve essere noto a compile-time!
  • 63. Passaggio per indirizzo (3) ¤  L’implementazione di sum_int_matrix è dunque: int sum_int_matrix(int (*matrix)[3], size_t row_num) { int sum = 0; for (size_t i = 0; i < row_num; ++i) for (size_t j = 0; j < 3; ++j) sum += matrix[i][j]; return sum; } ¤  L’operatore [] ci permette di manipolare il puntatore matrix con la stessa sintassi che useremmo per un vero array multidimensionale
  • 64. Sintassi alternativa ¤  Come per gli array, esiste una sintassi alternativa per acquisire l’indirizzo della prima riga di un array multidim. ¤  I tre seguenti prototipi di sum_int_matrix sono equivalenti: int sum_int_matrix(int (*matrix)[3], size_t row_num); int sum_int_matrix(int matrix[][3], size_t row_num); int sum_int_matrix(int matrix[3][3], size_t row_num); ¤  NOTA: tale equivalenza vale unicamente quando si dichiara una funzione
  • 65. Array e iteratori
  • 66. Iteratore (1) ¤  Un iteratore permette l’accesso sequenziale ad un container di elementi senza per questo esporne la struttura ¤  In questo modo: ¤  Il container ha il solo compito di utilizzare una strategia di memorizzazione per preservare gli elementi in memoria ¤  L’iteratore ha il solo compito di fornire uno strumento di accesso agli elementi ¤  Nel nostro caso il container è l’array
  • 67. Iteratore (2) ¤  Si può chiedere ad un iteratore: ¤  di recuperare un elemento ¤  di muoversi da un elemento all’altro ¤  Un iteratore è abbastanza potente da permettere di navigare il container con le sole funzionalità offerte dall’iteratore
  • 68. I puntatori sono iteratori ¤  I puntatori sono la forma più semplice di iteratore, essi possono infatti: ¤  Recuperare l’elemento puntato, mediante l’operatore *! ¤  Puntare all’elemento successivo, mediante l’aritmetica dei puntatori numbers: 1 7 13 numbers_iterator 5 int* 9 int[5]
  • 69. Iteratore di inizio e fine (1) ¤  Gli estremi di un container sono demarcati da due iteratori: ¤  Un iteratore posizionato in corrispondenza del 1° elemento ¤  Un iteratore in posizione successiva all’ultimo elemento numbers: 1 7 begin_iterator 13 5 9 end_iterator int[5] int*
  • 70. Iteratore di inizio e fine (2) ¤  Dato un array a di m elementi di tipo T, gli iteratori di inizio e fine sono ottenibili come: constexpr size_t DIM = 5; int numbers[] = {1, 7, 13, 5, 9}; int* begin_it = &numbers[0]; // // int* end_it = &numbers[DIM]; // // begin_it points to the first element end_it is a pointer just past the last element
  • 71. Iteratore di inizio e fine (3) ¤  Per begin_it è possibile usare il decadimento: int* begin_it = numbers; ¤  Per end_it è possibile utilizzare l’aritmetica dei puntatori: int* end_it = numbers + DIM; ¤  Il C++11 fornisce due funzioni per ottenere facilmente entrambi gli iteratori: int* begin_it = std::begin(numbers); int* end_it = std::end(numbers);
  • 72. Navigare l’array ¤  Una volta disponibili gli iteratori di inizio e di fine è possibile navigare l’array for (int* it = begin_it; it != end_it; ++it) std::cout << *it << std::endl; numbers: 1 begin_it 7 13 5 it 9 int[5] end_it int*
  • 73. Algoritmi standard (1) ¤  Gli iteratori possono essere utilizzati per compiere una moltitudine di operazioni sui container ¤  Tutti gli algoritmi standard del C++ accettano in ingresso una coppia di iteratori ¤  La coppia delimita la porzione del container su cui si vuole agire ¤  In questo modo ogni algoritmo può essere usato con un qualsiasi container, purchè esponga degli iteratori ¤  Esempio: std::vector, std::set, std::list!
  • 74. Algoritmi standard (2) ¤  Esempio: copiare un container int other_numbers[5]; std::copy(std::begin(numbers), std::end(numbers), other_numbers); ¤  Esempio: sommare i valori in un container int sum = std::accumulate(std::begin(numbers), std::end(numbers), 0); ¤  Il codice non sarebbe cambiato anche se numbers fosse stato un vector o un set!
  • 75. Array e funzioni Aspetti avanzati (facoltativo)
  • 76. La funzione sum_int_array_5! ¤  L’implementazione proposta di sum_int_array può elaborare array di qualsiasi dimensione ¤  In ingresso infatti la funzione accetta un puntatore al primo elemento dell’array, non l’array stesso ¤  Supponiamo di voler scrivere una funzione sum_int_array_5 che sommi unicamente array di dimensione 5! ¤  Non possiamo più accettare puntatori al primo elemento ¤  Dato un puntatore al primo elemento, non siamo in grado di dedurre la dimensione del corrispettivo array
  • 77. Preservare la dimensione ¤  Per mantenere traccia della dimensione, l’array non deve decadere in un puntatore ¤  D’altra parte non possiamo passare l’array per copia ¤  IDEA: passare un puntatore all’array, non un puntatore al primo elemento dell’array ¤  Passare alla funzione un puntatore a int[5] invece di un puntatore a int!
  • 78. Passaggio per indirizzo: puntatore ad array (1) ¤  La funzione sum_int_array_5 accetta come parametro in ingresso unicamente puntatori a tipo int[5]! int sum_int_array_5(int (*array_ptr)[5]); ¤  Nella funzione chiamante è necessario passare l’indirizzo dell’array int main() {
 
 int numbers[] = {1, 7, 13, 5, 9};
 int sum = sum_int_array_5(&numbers);
 
 }" È necessario usare l’operatore & per ottenere l’indirizzo di numbers!
  • 79. Passaggio per indirizzo: puntatore ad array (2) ¤  L’implementazione di sum_int_array_5 è dunque: int sum_int_array_5(int (*array_ptr)[5]) { int sum = 0; for (size_t i = 0; i < 5; ++i) sum += (*array_ptr)[i]; Non abbiamo l’array, ma un puntatore all’array. Dobbiamo usare l’operatore * per accedere all’array! return sum; } ¤  Si ottiene l’array dereferenziando array_ptr; a quel punto con l’operatore [] si accede agli elementi dell’array
  • 80. Passaggio per indirizzo: puntatore ad array (3) int sum_int_array_5(int (*array_ptr)[5]); ¤  Siamo soddisfatti di quanto realizzato? ¤  Bisogna gestire il passaggio di un puntatore nullo ¤  Siamo costretti nel corpo della funzione ad usare l’operatore di indirezione ovunque ¤  La risposta C++ a questo tipo di problemi è sempre la medesima, riferimenti
  • 81. Riferimento ad un array (1) ¤  Un riferimento ad array si dichiara in maniera similare a quanto già visto per i puntatori int numbers[] = {1, 7, 13, 5, 9}; int (&numbers_ref)[5] = numbers; // numbers_ref is another // name for numbers ¤  Le parentesi sono fondamentali int& array_of_ref[5] = ? // each element is an // alternative name of which // variable? ¤  Non è possibile dichiarare variabili di tipo T&[m]!
  • 82. Passaggio per riferimento (1) ¤  La funzione sum_int_array_5 può essere alterata per accettare in ingresso un riferimento ad array di 5 elementi di tipo int! int sum_int_array(int (&array_ref)[5]); ¤  Invocare la funzione è molto semplice: int main() {
 
 int numbers[] = {1, 7, 13, 5, 9};
 int sum = sum_int_array_5(numbers);
 
 }" L’espressione è corretta considerando numbers come un tipo int[5], il decadmento non interviene!
  • 83. Passaggio per riferimento (2) ¤  L’implementazione di sum_int_array_5 è dunque: int sum_int_array_5(int (&array_ref)[5]) { int sum = 0; for (size_t i = 0; i < 5; ++i) sum += array_ref[i]; return sum; } ¤  L’uso dei riferimenti rende semplice la lettura della funzione, che viene scritta come se l’array fosse stato passato per copia
  • 84. Passare array a funzioni ¤  Passare array a funzioni è più complicato di quanto dovrebbe essere, ¤  questo perchè gli array non posso essere passati per copia ¤  Regola del pollice, se la funzione: ¤  Non dipende dalla dimensione: passa un puntatore al primo elemento ¤  Dipende dalla dimensione: passa un riferimento all’array
  • 85. Bibliografia
  • 86. Bibliografia ¤  S. B. Lippman, J. Lajoie, B. E. Moo, C++ Primer (5th Ed.) ¤  B. Stroustrup, The C++ Programming Language (4th Ed.) ¤  The Gang of Four, Design Patterns - Elements of Reusable Object Oriented Software ¤  Stackoverflow FAQ, “How do I use arrays in C++?” http://stackoverflow.com/questions/4810664/how-do-iuse-arrays-in-c