Cpp Info fondamentali
From Aino Wiki
Test script C++ on-line con aiuto dell'AI: onecompiler.com
Contents
- 1 Fondamenta
- 1.1 Parole chiave
- 1.2 Funzione Main
- 1.3 Costanti
- 1.4 Variabili
- 1.5 Controllo del flusso
- 1.6 Cicli
- 1.7 Varie
- 1.8 Uso di Funzioni
- 1.9 Input/Output
- 1.10 Moduli o Librerie
- 2 Istruzioni o operatori particolari
- 3 Programmazione Avanzata
- 4 (Mappa e Link)
Fondamenta
IPOTESI conoscenza di altro liguaggio simile a C.
Per quanto riguarda il C usato nell'IDE di Arduino, la documentazione è qui: C language reference
Parole chiave
|
alignas |
auto |
union |
char16_t |
wchar_t |
Funzione Main
Ogni programma ha una funzione principale chiamatomain, il minimo per fare output o avere un input è includere la libreria fondamentale che è stdio (Standard Input Output), l'istruzione che lo fa è #include ed è chiamata "direttiva", la libreria è un file e l'etensione delle librerie è .h#include <stdio.h> int main(void){ printf("Ciao !"); return 0; }
Costanti
La costante con nome si definisce col qualificatore "const":const int nMax = 10; const double pg = 3.1415; const char separatore = -''; const char frase[] = "Giuseppe Aino";
La compatibilità con C consente la definizione di costanti simboliche sostituite dal "precompilatore". Una costante simbolica è definita mediante una "direttiva" che si definisce col simbolo #:
#define 3.1415 PI_GRECOCostanti particolari sono i caratteri speciali:
| Simbolo | Significato | Esempio |
|---|---|---|
| \n | new line, accapo | char parola[] = "Frase finisce qui\ne qui inizia..." |
| \r | return, va ad inizio rigo | |
| \t | tabulatore orizontalle | |
| \ooo | Numero ottale | |
| \xhh | Numero esadecimale |
Variabili
Tipi
Tipi semplici
| Tipo | Descrizione | Esempio |
|---|---|---|
byte
|
Numero intero che va da 0 a 255 | byte i = 0; |
int
|
Numerico intero | int contatore = 0; |
long
|
Numeri interi da -2.147.483.648 a -2.147.483.647 | long n = 100; |
float
|
Numero a virgola mobile da -3.4028235*10^38 a 3.4028235*10^38 | float temperatura = 1.12; |
char
|
Carattere. Si indicano con l'apice singolo ' . Array di caratteri posson esser trattate come stringhe ma la dimensione deve esser maggiorata di uno in quanto l'ultimo carattere deve il carattere speciale \0 NOTA il carattere null = 0x00 è usato come terminatore degli array di caratteri. |
char lettera = 'a'; char testo[5] = '1234'; |
|
|
Stringa |
string testo = "Bravo come sempre"; string testo2 = "Amico " + "mio."; |
| bool | Tipo booleano che rappresenta "vero" o "falso" |
bool test = true; |
Si usa il qualificatore
enumenum Giorno{lunedì,martedì,..,sabato,domenica};
Gli elementi tra parentesi graffe sono identificatori delle costanti del tipo.
Tipi puntatore
Riferiti a variabili e funzioni.
Il puntatore nullo è la parola chiave\letterale: nullptr, può essere utilizzato per inizializzare un puntatore, evitare di usare NULL.
MyVisualObject* newArr = nullptr; // ...etc newArr = new MyVisualObject[srcFlteredCount]; // ...etc if (m_ArrFilteredVisualObj != nullptr) { delete[] m_ArrFilteredVisualObj; }
Variabili puntatore
Innanzitutto definiamo due caratteri usati nei riferimenti a puntatori e variabili puntate:
- '*', usato per accedere ad un oggetto a cui un puntatore si riferisce. Se anteposto ad un nome di una variabile indica che questa sarà un puntatore ovvero un indirizzo di memoria che conterrà una variabile. Anche chiamato operatore di deferenziazione o indirezione.
- '&', se anteposto ad una variabile ne restituisce l'indirizzo di memoria (il puntatore) a questa variabile. Chiamato operatore di referenziazione o "address of".
int number = 1234; int *pointerI; //pointerI è un puntatore ad una variabile di tipo intero pointerI = &number; //pointerI conterrà l'indirizzo di memoria della variabile number *pointerI = 10; //modifica inserendo 10 nella memoria puntata da pointer ==> number sarà così diventato 10 char lettera; char *puntatoreC; //puntatoreC è un puntatore ad una variabile di tipo char
Altro esempio:
int i=0, j=0; int *p, *q; p=&i; //p conterrà l'indirizzo di i *p=3; //poiché p punta ad i, l'istruzione equivale a scrivere: i=3; j=*p; //è come scrivere assegna a j il contenuto puntato da p, equivale a j=i; q=p; //equivale a scrivere q=&i;
Con un puntatore è possibile, in teoria, raggiungere via software ognuna delle celle di memoria esistenti sul computer ospitante (è un tipo di codice non protetto), ma in pratica non è saggio farlo (e con i Sistemi Operativi moderni non è nemmeno possibile), poiché in taluni casi si rischia un crash di sistema.
Tentare di accedere al di fuori dello spazio di indirizzi riservato al programma genera una violazione, che il sistema operativo ci segnala con un messaggio di errore e con l'interruzione forzata del codice che stiamo eseguendo.
Tipi strutturati
Come array, struct, unione.
Array
ATTENZIONE
- C'è differenza tra C++ e C#, si usano le parentesi quadarate ma in C++ si mettono dopo il nome della variabile mentre in C# si mettono dopo il nome del tipo;
- Il numero tra le parentesi quadrate è la dimensione in base 1,
int arr[5];significa che arr[5], cinque elementi.
Inizializzazioni
//Array di interi di dimensione non definita (lo definirà il compilatore ! ): int arrInt[] = {10, 20, 30, 40}; // di dimensione fissa int arrCinquinaTombola[5] = {1, 2, 3, 4, 5}; //Array di numeri reali: float v[3] = {0.1, 0.2, 0.3}; //Array di stringhe String arrCars[] = {"Volvo", "BMW", "Ford", "Mazda"}; //Accesso ad elemento: Console.WriteLine(arrCars[0]); // Outputs Volvo
Array bidimensionali:
int matrice[2,3] = { {11, 12, 13}, {21, 22, 23} };
Se in un array di dimensione n si inizializza con meno elementi della dimensione i valori successivi sono inizializzati in modo casuale.
Operazioni tipiche
Ottenere la dimensione:int a[7]; std::cout << "Length of array = " << (sizeof(a)/sizeof(*a)) << std::endl; //Oppure: int b[17]; size_t n = sizeof(b)/sizeof(b[0]);
In pratica si ottiene la dimensione in byte occupata dall'intera struttura della variabile array e si divide per l'occupazione in byte di un singolo elemento. Infatti:
int a[7]; //Per un singolo l'intero array: std::cout << "Length of array = " << sizeof(a) << std::endl; // Restituisce 28 //Per un singolo elemento dell'array (dimensione del puntatore a* ): std::cout << "Length of array = " << sizeof(*a) << std::endl; // Restituisce 4 //Quindi la dimensione di a è: 28/4 = 7
Si può anche usare una finezza, dichiarare una macro per cui per qualsiasi tipo di array basterà:
#define NELEMS(x) (sizeof(x) / sizeof((x)[0])) int a[17]; size_t n = NELEMS(a);
Se è vuoto o meno:
In C++, non esiste un modo diretto per verificare se un array è vuoto o meno. Gli array in C++ contengono la quantità di memoria assegnata e questa memoria conterrà alcuni valori inutili anche quando non assegniamo alcun valore valido agli elementi dell'array. Inoltre, non esiste un indicatore che indichi se il valore specificato è assegnato dall'utente o è un valore inutile.
Ma possiamo avere alcuni metodi per tenere traccia del conteggio dei valori, il che può aiutare a verificare se l'array è vuoto. Uno di questi è il mantenimento di un puntatore che tiene traccia della posizione dell'ultimo elemento presente nell'array. Un altro è l'utilizzo di un nodo sentinella per rappresentare la fine dell'array.
L'esempio seguente mostra come il tracciamento della posizione dell'ultimo elemento aiuti a verificare.
// C++ Program to illustrate how to check if an array is // empty #include <iostream> using namespace std; // Define a class named Array class Array { int* arr; // Pointer to the first element of the array int* end; // Pointer to the last element of the array int capacity; // Size of the array public: // Constructor that initializes the array, end pointer // and capacity Array(int size) { arr = new int[size]; end = arr; capacity = size; } // Function to insert an element at the end of the array void insert(int val) { if (end - arr < capacity) { *end = val; end++; } else { cout << "Array is full.\n"; } } // Function to check if the array is empty bool isEmpty() { return end == arr; } }; int main() { // Create an array of size 5 Array a(5); // Check if the array is empty and print a message // accordingly if (a.isEmpty()) { cout << "Array is empty.\n"; } else { cout << "Array is not empty.\n"; } return 0; }
Puntatori ed array
Ricordiamo qui i due caratteri usati nei riferimenti a puntatori e variabili puntate:
- '*', usato per accedere ad un oggetto ad esempio ad un array di caratteri. Se anteposto ad un nome di una variabile indica che questa sarà un puntatore ovvero un indirizzo di memoria che conterrà una variabile. Anche chiamato operatore di deferenziazione o indirezione.
- '&', se anteposto ad una variabile ne restituisce l'indirizzo di memoria (il puntatore) a questa variabile. Chiamato operatore di referenziazione o "address of".
char arrCaratteri[15] = "Salve Peppino."; char *p; //E' un PUNTATORE a variabile di tipo char (volendo anche il 1°elemento di un array di caratteri) //Ora SI ASSEGNA a p l'indirizzo dell'array !!! p = arrCaratteri; //p punterà alla testa dell'array ovvero al carattere 'S' //DA QUI IN POI scrivere *p è come scrivere arrCaratteri[0] !!! //Si verifica infatti: printf("(Valore puntatore p)=%p - contenuto variabile puntata %c - %c\n", (void*)p, *p, arrCaratteri[0]); // ^-- OUTPUT: (Valore puntatore p)=000000933DFAFC68 - contenuto variabile puntata S - S
NOTARE che per avere il 5°elemento (si parte da 0) dell'array di caratteri ci sono due modi:
-
char mioCarattere = arrCaratteri[4];metodo leggibile; -
char mioCarattere = *(p + 4);metodo più rapido (e qui per far capire il funzionamento di *p). - Per definire una variabile puntatore l'asterisco si può mettere anche attaccato al tipo, es.
char* p; //Uguale a char *p;
Il seguente codice oltre a far capire come si puntano gli array, è un esempio spinto di ottimizzazione del codice, in register int i; (che poteva anche funzionare comunque con int i;) si ordina al compilatore di mantenere la variabile i direttamente in un registro della CPU.
#include <iostream> void StampaTesto(char* str) { // str è un puntatore a char register int i; for (i = 0; str[i]; i++) { // alla fine str[i] == '\0' ed equivale a false !!! putchar(str[i]); } } void StampaTesto_UsandoPuntatore(char* str) { // str è un puntatore a char while (*str) { // alla fine *str == '\0' ed equivale a false !!! putchar(*str++); // Stampa il carattere contenuto all'indirizzo puntato da str e gli aggiunge 1 } } int main() { char testo[] = "Salve Peppino"; StampaTesto(testo); putchar('\n'); StampaTesto_UsandoPuntatore(testo); std::cout << "\n\nPremi invio per terminare..."; std::cin.get(); return 0; }
Operazioni complesse
NOTA Nativamente in C++ non esistono gli array dinamici, ma si possono simulare.
Aggiunta dinamica di un elemento ad un array di interi:
#include <iostream> using namespace std; // Function to add an item at the end of the array and increase its size void addItem(int *&arr, int &size, int newItem) { int *newArr = new int[size + 1]; for (int i = 0; i < size; i++) { newArr[i] = arr[i]; } newArr[size] = newItem; delete[] arr; arr = newArr; size++; } int main() { int size = 3; int *arr = new int[size]{10, 20, 30}; cout << "Original array: "; for (int i = 0; i < size; i++) { cout << arr[i] << " "; } cout << endl; addItem(arr, size, 40); cout << "Array after adding item: "; for (int i = 0; i < size; i++) { cout << arr[i] << " "; } cout << endl; delete[] arr; return 0; }
Output:
Original array: 10 20 30 Array after adding item: 10 20 30 40
Aggiunta dinamica di un elemento ad un array di OGGETTI:
..esempio reale!
#include <iostream> using namespace std; enum PictureType { Text, Rectangle, Square, Circle, Triangle }; enum PictureSubType { Number, Letter, Symbol, Shape }; class MyVisualObject { public: //--------------- Attributi ---------------- string Label; //Opzionale nel caso l'oggetto da disegnare sia grafico e non testo PictureType PicType; //Enum., indica il tipo oggetto: testo, rettangolo, cerchio, triangolo PictureSubType PicSubType; //Enum., indica il sottotipo di ciascun tipo: numero, lettera, simbolo, forma int Color; //Colore testo o riempimento. Valore di es. 0xF81F, vedi file Adafruit_ILI9341.h int OutlineColor; //Colore bordo (solo per oggetti grafici) int X1, Y1; //Punto in alto a sx x quadrato, vertice superiore triangolo, centro cerchio o inizio testo int X2, Y2; //Punto in basso a sx per triangolo int X3, Y3; //Punto in basso a dx per triangolo int H, W; //Altezza + larghezza rettangolo int R; //Raggio cerchio //--------------- END Attributi ------------ //Costruttori: MyVisualObject() {} MyVisualObject(string label, PictureType picType, PictureSubType picSubType , int color, int outlineColor , int x1, int y1 , int x2 = 0, int y2 = 0 , int x3 = 0, int y3 = 0 , int w = 0, int h = 0 , int r = 0) { Label = label; PicType = picType; PicSubType = picSubType; Color = color; OutlineColor = outlineColor; X1 = x1; Y1 = y1; X2 = x2; Y2 = y2; X3 = x3; Y3 = y3; W = w; H = h; R = r; } }; MyVisualObject m_ArrVisualObj[11]; void FillArrayVisualObj() { m_ArrVisualObj[0] = MyVisualObject("0", PictureType::Text, PictureSubType::Number, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); m_ArrVisualObj[1] = MyVisualObject("1", PictureType::Text, PictureSubType::Number, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); m_ArrVisualObj[2] = MyVisualObject("2", PictureType::Text, PictureSubType::Number, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); m_ArrVisualObj[3] = MyVisualObject("3", PictureType::Text, PictureSubType::Number, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); m_ArrVisualObj[4] = MyVisualObject("3", PictureType::Text, PictureSubType::Number, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); m_ArrVisualObj[5] = MyVisualObject("3", PictureType::Text, PictureSubType::Number, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); m_ArrVisualObj[6] = MyVisualObject("3", PictureType::Text, PictureSubType::Number, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); m_ArrVisualObj[7] = MyVisualObject("7", PictureType::Text, PictureSubType::Number, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); m_ArrVisualObj[8] = MyVisualObject("8", PictureType::Text, PictureSubType::Number, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); m_ArrVisualObj[9] = MyVisualObject("9", PictureType::Text, PictureSubType::Number, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); m_ArrVisualObj[10] = MyVisualObject("X", PictureType::Text, PictureSubType::Letter, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); } // Function to add an item at the end of the array and increase its size // Properly delete the old array to avoid memory leak and ensure arr points to the new array void AddItem(MyVisualObject*& arr, int& size, MyVisualObject newItem) { MyVisualObject* newArr = new MyVisualObject[size + 1]; for (int i = 0; i < size; i++) { newArr[i] = arr[i]; } newArr[size] = newItem; if (arr != m_ArrVisualObj) { // Only delete if arr is not pointing to the static array delete[] arr; } arr = newArr; size++; std::cout << "AddItem() fine funzione" << std::endl; } int main() { FillArrayVisualObj(); int size = sizeof(m_ArrVisualObj) / sizeof(m_ArrVisualObj[0]); std::cout << "Lunghezza di m_ArrVisualObj = " << size << std::endl; MyVisualObject* arrVisualObj; arrVisualObj = m_ArrVisualObj; MyVisualObject newItem = MyVisualObject("?", PictureType::Text, PictureSubType::Symbol, 9999, 9999, 90, 10, 0, 0, 0, 0, 0, 0, 0); std::cout << "Creato nuovo item" << std::endl; AddItem(arrVisualObj, size, newItem); std::cout << "1 NUOVA Length of array = " << size << std::endl; for (int i = 0; i < size; i++) { cout << "Elemento " << i << " = " << arrVisualObj[i].Label << "\n"; } //delete[] arrVisualObj; std::cout << "Fine" << std::endl; std::cout << "\n\nPremi INVIO per terminare..."; std::cin.get(); return 0; }
In alternativa per l'aggiunta dinamica di elementi ad un array si può usare la libreria STL con l' uso di una nuova struttura std::vector che non è più un array standard.
#include <iostream> #include <vector> using namespace std; int main() { std::vector<int> arr; arr.push_back(11); arr.push_back(200); arr.push_back(333); arr.push_back(1); int n = arr.size(); cout << "Dimensione array = " << n << " , il cui contenuto è:\n"; for (int i=0; i<n; i++) { printf("Indice = %d, valore %d\n", i, arr[i]); } return 0; }
Stringhe Libreria cstring
Libreria cstring
Con la libreria cstring si possono usare le seguenti funzioni nello stile C (terminanti con 0):
| Funzione | Descrizione | Esempio |
|---|---|---|
strlen(array)
|
Restituisce il numero di caratteri validi | i=strlen(array); |
strstr()
|
per le ricerche | |
strcmp()
|
per la comparzione | |
strcat(destinazione,sorgente)
|
Concatena la Sorgente alla fiine della destinazione | |
strcpy(destinazione,sorgente)
|
Per la copia di array | strcpy(array,"test"); |
strcmp(str1, str2)
|
Comparazione di stringhe | #include <iostream> #include <cstring> using namespace std; int main() { char str1[] = "Apple"; char str2[] = "Banana"; int result = strcmp(str1, str2); if (result == 0) { cout << "Strings are equal." << endl; } else if (result < 0) { cout << "'" << str1 << "' is less than '" << str2 << "'." << endl; } else { cout << "'" << str1 << "' is greater than '" << str2 << "'." << endl; } return 0; } |
Struct o Record
Trovano analogia in un record di un database:
struct numComplesso { float parteReale; float coefficienteImmaginario; };
Pertanto per riferisvisi:
struct numComplesso { float parteReale; float coefficienteImmaginario; }; // Ecco come vi si accede agli elementi della struttura e come si valorizza: numComlesso x; // x è variabile di tipo numComplesso //Valorizzazione: x = {3.5, 4.6}; //Oppure x.parteReale = 3.5; x.coefficienteImmaginario = 4.6; //Acccesso printf(x.parteReale); //Oouput: 3.5 printf(x.coefficienteImmaginario); //Oouput: 4.6
Output
L'output a schermo è così prodotto in base al tipo di variabile di cui stampare il contenuto.//Per stampare l'output di un numero: int i = 12; printf("Contatore = %d", i); //Per stampare l'output di un carattere: char carattere = 'x'; printf("Carattere = %c", carattere); //Per stampare l'output di un vettore di caratteri: char testo[] = "Il suno del silenzio"; printf("Testo = %s", testo); //Per stampare l'output di un numero float: float risultato = 10.1 / 2.2; printf("Risultato = %f", risultato); // ancora float risultato = (float)10 / (float)2; printf("Risultato = %f", risultato); //Per stampare l'output di una variabile booleana: bool vero = false; printf("Risultato = %d", vero); //Stamperà 0 printf("Risultato = %d", !vero); //Stamperà 1
\n
Operazioni
Stringhe di caratteri
// Ricordarsi che una stringa di caratteri DEVE finire con \n char Str3[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o', '\0'};
Stringhe x Arduino
[1] Sintassi:
String(val) String(val, base) // base: DEC, HEX, BIN String(val, decimalPlaces)
String stringOne = "Hello String"; // using a constant String String stringOne = String('a'); // converting a constant char into a String String stringTwo = String("This is a string"); // converting a constant string into a String object String stringOne = String(stringTwo + " with more"); // concatenating two strings String stringOne = String(13); // using a constant integer String stringOne = String(analogRead(0), DEC); // using an int and a base String stringOne = String(45, HEX); // using an int and a base (hexadecimal) String stringOne = String(255, BIN); // using an int and a base (binary) String stringOne = String(millis(), DEC); // using a long and a base String stringOne = String(5.698, 3); // using a float and the decimal places
Operazioni
Nel seguente esempio c'è la concatenazione anche con un numero e l'append.//Concatenazione String myString1 = "Pizza "; String myString2 = "Margherita"; String myString3 = myString1 + myString2; //Concatenazione stringa numero int numero = 0; String myString3 = myString1 + " di " + (String)numero + myString2; //Append String myString1 = "Pizza "; String myString2 = "ai 4 formaggi"; myString1 += myString2;
Data e ora
Utilizzando la libreria ctime.
Metodo con l'utilizzo del timestamp
Si usa meno memoria sfruttando la rappresentazione numerica della data.
#include <iostream> //Per l'output a video #include <ctime> //Per lavorare con le date using namespace std; int main () { time_t dataOrario; //Variabile di tipo Data e Ora time(&dataOrario); //FUNZIONE che restituisce il timestamp //La funzione ctime() per rappresentare la data dell'oggetto timestamp cout << "Data Orario: \r\n"; // Visualizza la data in formato misto, testo e numeri ma non nel fuso orario corrente... cout << ctime(&dataOrario); // Output es: Thu Dec 11 10:10:22 2025 return 0; }
Metodo con l'utilizzo della struttura data
Metodo più orientato alla lettura ed customizzazione.
#include <iostream> #include <ctime> // Import the ctime library using namespace std; int main() { time_t timestamp = time(NULL); struct tm datetime = *localtime(×tamp); char output[50]; strftime(output, 50, "%B %e, %Y", &datetime); //OUT: December 11, 2025 cout << output << "\n"; strftime(output, 50, "%I:%M:%S %p", &datetime); //OUT: 10:06:43 AM cout << output << "\n"; strftime(output, 50, "%m/%d/%y", &datetime); //OUT: 12/11/25 cout << output << "\n"; strftime(output, 50, "%a %b %e %H:%M:%S %Y", &datetime); //OUT: Thu Dec 11 10:06:43 2025 cout << output << "\n"; // !!! //Data Italiana: gg/mm/yyyy hh:MM:SS strftime(output, 50, "%d/%m/%Y %I:%M:%S %p", &datetime); //OUT: 11/12/2025 10:06:43 AM cout << output << "\n"; return 0; }
Controllo del flusso
if
if (x > 120) digitalWrite(LEDpin, HIGH); if (x > 120) {digitalWrite(LEDpin, HIGH);} if (x > 120) { digitalWrite(LEDpin1, HIGH); digitalWrite(LEDpin2, HIGH); }
switch case
Una specie di multi if corrispondente all'istruzione switch case:char[20] strValue = "xxx"; switch (strValue) { case "Caso 1": case "Caso 2": case "Caso 3": printf("Caso 1,2,3"); break; case "Caso 4": printf("Caso 4"); break; case "Caso 5": printf("Caso 5"); break; case "Caso ok": break; default: break; } // Sui numeri: int i = 6; switch (i) { case 1: printf("Caso 1"); break; case 2: printf("Caso 1"); break; case 3: printf("Caso 1,2,3"); break; case 4: printf("Caso 4"); break; default: break; }
Cicli
for
for (int i = 1; i <= 5; i++) { printf("%i", i); }
While loop
int n = 1; while (n < 6) { printf("Current value of n is %d", n); n++; // break; // <--- esce dal ciclo while ! }
Do While Loop
int i = 0; do { //Operazioni con l'indice i // ... etc ... i++; } while (i < 7);
Varie
Operatore ternario
Anzichè usare if then else. Sintassi:
(condizione ? valore_se_vero : valore_se_falso)
int value1 = 0; int value2 = 5; int value3 = 10; // Si potrebbe usare 0 come valore "nullo" per gli interi, anche se non è un vero "NULL" int result_value = (value1 != 0) ? value1 : ((value2 != 0) ? value2 : value3);
Uso di Funzioni
Nel segeunte esempio main() è la funzone di partenza che userà la nostra funzione custom somma(), somma() ha due parametri interi passati (per valore ovvero che non cambieranno in Main()):
#include <iostream> // Dichiarazione della funzione (prototipo) !!! // Specifica che la funzione si chiama 'somma', restituisce un 'int' // e accetta due parametri 'int' chiamati a e b. int somma(int a, int b); int main() { int risultato; // Chiamata della funzione 'somma' con gli argomenti 5 e 3. risultato = somma(5, 3); //Stampa in output: std::cout << "Il risultato della somma e': " << risultato << std::endl; return 0; } // Definizione della funzione int somma(int a, int b) { // 'a' e 'b' sono variabili locali alla funzione, inizializzate con i valori passati. int r; r = a + b; // La parola chiave 'return' restituisce il valore di 'r' al chiamante (main). return r; }
NOTARE che in C++ a meno di non dichiararle prima in una sequenza che rispetti l'uso occorre specificare sempre le funzioni da usare nella classe, questa specifica iniziale si chiama definizione del prototipo, quindi prima si dichiarano i prototipi (in inglese "prototype", function's signature declaration) e dopo seguerà la loro effettiva implementazione.
Lo scopo, che con i moderni linguaggi è superfluo, è anticipare al compilatore dell'esistenza e dei dettagli di funzioni che si andaranno a definire nella classe in cui ci si trova.
Output complesso
Esempio di funzione con un array in output
In realtà restituisce un puntatore ad un array in output, e richiede la gestione della memoria per prevenire lo spreco di memoria.
#include <iostream> // Function returns a pointer to dynamically allocated memory int* createDynamicArray(int size) { int* arr = new int[size]; // Allocate memory on the heap for (int i = 0; i < size; ++i) { arr[i] = i * 10; // Initialize elements } return arr; // Return the pointer to the first element } int main() { int size = 4; int* myArray = createDynamicArray(size); // Get the pointer std::cout << "Dynamic array elements: "; for (int i = 0; i < size; ++i) { std::cout << myArray[i] << " "; } std::cout << std::endl; delete[] myArray; // IMPORTANT: Deallocate the memory! return 0; }
Esempio di funzione con array std::vector<code> in output
Metodo raccomandato per un approccio sicuro e flassibile che tra la'ltro gestisce automaticamente la memoria.
#include <iostream> #include <vector> // Include the vector header // Function returns a vector of integers std::vector<int> createVectorArray() { std::vector<int> vec = {10, 20, 30, 40, 50}; return vec; // Vector is copied (or moved) on return } int main() { std::vector<int> myArray = createVectorArray(); // Get the vector std::cout << "Vector elements: "; for (int val : myArray) { std::cout << val << " "; } std::cout << std::endl; // No need to manually delete memory! return 0; }
Passaggio per referenza
Esempio semplice#include <iostream> using namespace std; // Definizione del prototipo int moltiplica(int& x, int& y); void main() { int a = 5; int b = 6; int risultato; //Passagio per referenza con & risultato = moltiplica(a, b); cout << Il risultato della moltiplicazione è: << risultato <<endl; return (0); } // Dichiarazione della funzione int moltiplica(int& x, int& y) { int ris; ris = *x * *y; return ris; }
Esempio di passaggio di un array per referenza
#include <iostream> using namespace std; const int DIM_ARR = 5; void modifyArray(int (&arr)[DIM_ARR]) { // Determinazione della dimensione int size = sizeof(arr) / sizeof(int); for (int i = 0; i < size; ++i) { arr[i] *= 2; } } int main() { int arr[] = { 1, 2, 3, 4, 5 }; modifyArray(arr); for (int i = 0; i < DIM_ARR; i++) { cout << arr[i] << " "; } return 0; }
Esempio di passaggio di un oggetto per referenza
Anche in questo caso si usa la & allo specifico parametro della funzione MyClass& obj per dare alla funzione un alias dell'oggetto originale senza copiarlo ma consentendo una bodifica diretta con conseguente efficienza.
#include <iostream> #include <string> class Car { public: std::string model; int speed; Car(std::string m, int s) : model(m), speed(s) {} }; // Passaggio PER REFERENZA usando & void increaseSpeed(Car &c) { c.speed += 20; // This modifies the original object in main() } int main() { Car myCar("Tesla", 100); std::cout << "Original Speed: " << myCar.speed << " mph" << std::endl; // Call function normally (syntax is the same as pass-by-value) increaseSpeed(myCar); // !!! Chiamata funzone per referenza std::cout << "New Speed: " << myCar.speed << " mph" << std::endl; return 0; }
PARTICOLARE ESEMPIO, nel caso si voglia efficienza nell'accesso ad un oggetto ma passarlo read-only:
//Collegato all'esempio di prima !!! void printCarDetails(const Car &c) { // c.speed += 10; // ERROR: Cannot modify a const reference std::cout << c.model << " is going " << c.speed << " mph." << std::endl; }
Esempio di array di oggetti passati per referenza:
Non cambia nulla rispetto agli altri esempi tranne l'indicazione del passaggio per referenza fatta con le parentesi tonde: <code>void modifyArray(MyObject (&arr)[3]) {
#include <iostream> class MyObject { public; int value; MyObject(int v = 0) : value(v) {} void print() const { std::cout << "Value: " << value << " "; } }; // Function takes a reference to a 3-element array of MyObject void modifyArray(MyObject (&arr)[3]) { // Notice the '&' and size [3] std::cout << "Inside modifyArray (by reference):\n"; for (int i = 0; i < 3; ++i) { arr[i].value *= 2; // Modifying original objects arr[i].print(); } std::cout << "\n"; } int main() { MyObject objArray[3] = {MyObject(1), MyObject(2), MyObject(3)}; std::cout << "Before modification:\n"; for(const auto& obj : objArray) obj.print(); std::cout << "\n"; modifyArray(objArray); // Pass the array name std::cout << "After modification:\n"; for(const auto& obj : objArray) obj.print(); std::cout << "\n"; return 0; }
Parametri di default
Si può definire una funzione preassegnando di default un volore ai parametri in inputvoid MiaFunzione(int numero1=1, char lettera='a') { //... }
Overload
Si può definire più funzioni ocn lo stesso nome a patto che i parametri siano diversi per numero o tipo.void MiaFunzione(int numero1) { //... } void MiaFunzione2(int numero1, int numero2) { //... }
Input/Output
In modo basico per lavorare con input ed output in console si usa la libreria iostream:
#include <iostream>Output
Per produrre risultati al dispositivo standard di output (generalmente il monitor) si usa l'oggetto cout. L'operatore di inserimento (<<) è usato per inserire dati nello stream (flusso) di output.
#include <iostream> using namespace std; int main() { cout << "Ciao provolone"; // Stamperà a video: Ciao provolone //Invece per comporre un risultato da più variabili: int n = 42; string stringa = "La risposta è: "; cout << stringa << n; //Stamperà: La risposta è 42 return 0; }
Moduli o Librerie
I moduli sono file contenitori di funzioni utili spendibili ove necessario nel progetto.
L'uso dei moduli rende il codice più leggero anche in termini di leggibilità oltre che di "riusabilità".
Per creare un modulo ed includere le sue risorse in un file .cpp occorrono una coppia di files che devono rispettare una naming convention:
-
modulo.hanche definito header, sostanzialmente è una descrizione del modulo, ci sono la definizione dei prototipi delle funzioni implementate effettivamente nel secondo file che ha stesso nome ed estensione .cpp; -
modulo.cpp, file con l'implementazione del modulo (qualcuno lo chiama file "servente").
File header: MioModulo.h
extern const int miaCostanteEsportabile; // es. costante esportabile extern int g_NrMassimo; // Variabile globale esportabile typedef ... MioTipo; extern void MiaFunzione1(int parametro1, char parametro2); //Esempio di procedura con 2 parametri IN extern int MiaFunzione2(int parametro1, long parametro2); //Esempio di funzione con 2 parametri IN con output
Implementazione: MioModulo.cpp
#include "MioModulo.h" const int miaCostanteEsportabile = 10; // Definizione della costante esportata int g_NrMassimo; // Definizione variabile globale cha assumerà 0 per default static int m_varLocale; // Definizione variabile locale non esportata\visibile all'esterno static long MiaFunzioneLocale() { long varOutput = 0; //Implemetazione return varOutput; } void MiaFunzione1(int parametro1, char parametro2) { //Implemetazione } int MiaFunzione2(int parametro1, long parametro2) { int miaVarOutput = 0; //Implemetazione return miaVarOutput; }
Uso in file client: main.cpp
#include "MioModulo.h" //Il compilatore sa che l'implementazione è nel file MioModulo..cpp void main() { int variabile1 = 1; int variabile2 = 22; char variabile3 = 'a'; long variabile4 = 5; //implementazione MiaFunzione1(variabile1, variabile3); int risultato = MiaFunzione2(variabile2, variabile4); //implementazione }
NOTA abbiamo supposto che il modulo con i due files .h e .cpp siano nella stessa cartella del file client main.cpp altrimenti nell' #include occorre anticipare il nome con il path del file.
Istruzioni o operatori particolari
Customizzazioni
typedef
Da geeksforgeeks.org
E' una parola chiave usata per "aggiungere" (creare un alias) un nome diverso ad un tipo esistente, allo scopo di rendere più friendly il codice.
Sintassi
typedef current_name new_nam
Es. nel codice seguente il tipo "int" si vuol chiamare anche "Integer".
#include <stdio.h> typedef int Integer; int main() { // n is of type int, but we are using // alias Integer Integer n = 10; printf("%d", n); return 0; }
Esempio in cui si definisce un nuovo data type!
#include <bits/stdc++.h> using namespace std; int main() { // Giving new name // of vector<int> typedef vector<int> vInt; // vec1 is a vector of // type int vInt v; v.push_back(190); v.push_back(180); v.push_back(10); v.push_back(10); v.push_back(27); for (auto X : v) { cout << X << " "; } return 0; }
Operatori
:: o scope resolution
Viene utilizzato per accedere a membri di uno namespace, una classe o una struttura, anche se sono nascosti da un nome locale.
- Accesso a membri di namespace:
std::coutaccede alla variabile cout nel namespace std. - Accesso a membri statici di classe:
MyClass::myStaticMemberaccede al membro statico myStaticMember della classe MyClass. - Specificare il namespace di origine: permette di disambiguare nomi identici definiti in scope diversi. Ad esempio, se si definisce una funzione locale con lo stesso nome di una funzione globale, si può usare
::per chiamare la versione globale. - Definire membri di classe fuori dalla classe: permette di definire la funzione di una classe o struttura al di fuori della sua dichiarazione, specificando il nome della classe che la contiene:
MyClass::myMethod() { ... }. - Definire l'ambito della definizione: permette anche di definire i membri di una classe in un namespace. Ad esempio,
MyNamespace::MyClass::myMethod() { ... }definisce myMethod come membro di MyClass, che a sua volta è in MyNamespace.
Operatori puntatore
Sono due:
- Operatore indirizzo, &, usato come dichiaratore di riferimento (nei parametri formali di una funzione) oltre che come operatore di indirizzamento (es.
b=&a;b conterrà l'indirizzo di a). - Operatore indirezione, *, è il puntatore ad una variabile (es.
y=*b;stà per il valore contenuto all'indirizzo b).
Entrambe & e * sono operatori unari che precedono il loro operando. Quindi *&x è praticamente equivalente a x stesso.
Esempio d'uso *&:
//Esempio di aggiunta dinamica di un elemento ad un array void addItem(int *&arr, int &size, int newItem) { int *newArr = new int[size + 1]; for (int i = 0; i < size; i++) { newArr[i] = arr[i]; } newArr[size] = newItem; delete[] arr; arr = newArr; size++; } //uso: int size = 3; int *arr = new int[size]{10, 20, 30}; addItem(arr, size, 40);
In C++, *& dopo un tipo, come in tipo_dato*& parametro, significa che il parametro è un puntatore (il *) che viene passato per riferimento (il &), permettendo alla funzione di modificare sia il puntatore stesso (cioè l'indirizzo a cui punta) sia l'oggetto puntato. È una combinazione potente che consente alla funzione di "cambiare idea" su dove puntare e/o di manipolare direttamente il dato originale tramite quel puntatore, a differenza di un semplice tipo_dato* (passato per valore) che passerebbe una copia del puntatore.
Programmazione Avanzata
Controllo dell'errore
Nel seguente caso si gestisce l'eccezione generica ma c'è anche la possibilità di entrare nello specifico di una determianta classe di errori (es.: networkIOException).
int main() { try { //...codice da eseguire... std::cout << "Fine" << std::endl; } catch (const std::exception& ex) { std::cout << "Eccezione: " << ex.what() << std::endl; } std::cout << "\n\nPremi INVIO per terminare..."; std::cin.get(); return 0; }
Callback functions
In C#, evoluzione del C++, si chiamano Delegati (guida interna Delegati C#), usati anche negli haldlers.
Una funzione callback è una funzione passata ad un'altra utilizzando un puntatore a funzione questo perché il nome della funzione reale non può esser passato direttamente con questa complicanza si ottiene però una comodissima dinamicità.
Nel seguente esempio prima si definirà una funzione reale "ConvertiCarattere" che prenderà il posto di quella simbolica usata nella funzione "PrintASCIIcode()". Quindi, in PrintASCIIcode() si fa riferimento simbolicamente a "func_ptr" il cui compito definito è restituire un intero accettando in input un parametro di tipo char. NOTA il motivo per cui il meccanismo della callback è proprio nella definizione del parametro formale in input è proprio perché è lì il suo confine con l'esterno e lo fa con una sintassi in cui figura il carattere '*':
int(*func_ptr)(char)
Notare che il riferimento alla funzione concreta lo si fa tramite l'operatore puntatore "&"
, vedere nella funzione main() dell'esempio che segue.
Esempio completo:
#include <iostream> using namespace std; // Funzione di callback che concretamente farà una operzione int ConvertiCarattere(char c) { return (int)c; } // Segue una funzione che ne usa un'altra come se fosse un simbolo "func_ptr", // la concretezza di quanto richiesto da "func_ptr" la svolgerà fisicamente una // funzione che nel nostro caso specifico sarà ConvertiCarattere(). // Il momento in cui ConvertiCarattere() sarà usata è nella funzione main() in // cui figurerà come parametro di PrintASCIIcode() void PrintASCIIcode(char c, int(*func_ptr)(char)) { int ascii = func_ptr(c); // Stampa in output il codice ASCII del carattere passato cout << "ASCII code of '" << c << "' is: " << ascii; } // Esempio di utilizzo dinamico di una funzione int main() { PrintASCIIcode('a', &ConvertiCarattere); return 0; }
Output attraverso l'istruzione cout
ASCII code of 'a' is: 97
Programmazione ad Oggetti
Uso delle classi
Praticamente le classi possono assolvere ad una funzione preziosissima come contenitore di informazioni strutturate e relativi metodi per trasformarle e renderle fuibili.
NOTA in C++ l'uso delle risorse va fatto dopo la loro definizione ed implementazione. Quindi se si definiscono delle classi ad es. ad uso di classe principale, vanno implementati su prima dell'uso diretto che se ne farà giù.
Nel seguente caso si definisce una classe deputata a contenere delle informazioni relative a dei pulsanti definiti su uno schermo, i pulsanti hanno una regione grafica sulla quale si agirà per la selezione, la selezione produce delle coordinate che andranno verificate se ricadono nel rettangolo del pulsante.
//... classe contenitore principale { //---------------CLASSI contenute: class MyButton { //Attributi della classe public: int x; int y; int width; int height; String label; // Constructors MyButton(){} // Va anche definito il costruttore di default... Ad es. non necessario in C# MyButton(int xPos, int yPos, int w, int h, String lab) { x = xPos; y = yPos; width = w; height = h; label = lab; } // Qui si potrebbero anche definire dei metodi di helper }; //--------------(fine) CLASSI contenute----------- //.. MyButton m_Btns[16]; // Per comodità ci conserviamo i parametri di posizione di ogni bottone //.. void MiaProcedura1 (){ // Solo a scopo di esempio... non tutto è definito... int x = 0; for (int col=0; col<=4; col++){ x = c_leftMargin + (col * (c_buttonWH + c_buttonSpacing)); btnLbl = (String)col; // DEFINIZIONE degli elementi array che son CLASSI opportunamente istanziate con info individuali m_Btns[col] = MyButton(x, buttonY, c_buttonWH, c_buttonWH, btnLbl); } } // etc... void MiaProcedura2 (){ for (int i=0; i<=4; i++){ // Uso degli oggetti\classi instazioate in m_Btns // La funzione booleana CheckCollision() servirà a restituire se un determinato punto è nella regione del pulsante if (CheckCollision(sp.x , sp.y , 4 , 4, m_Btns[i].x, m_Btns[i].y, m_Btns[i].width, m_Btns[i].height)){ //... } } } }
(Mappa e Link)
Arduino C++ | C# | Visual Studio | MS SQL | Dizionario IT | Dizionario
Parole chiave:
