Login Login
MORE

WIDGETS

Widgets

Wanted articles
Who is online?
Article tools

Cpp Info fondamentali

From Aino Wiki

Jump to: navigation, search

Test script C++ on-line con aiuto dell'AI: onecompiler.com

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
continue
friend
register
true
alignof
decltype
goto
reinterpret_cast try
asm
default
if
return
typedef

auto
delete
inline
short
typeid
bool
do
int
signed
typename
break
double
long
sizeof

union
case
dynamic_cast mutable
static
unsigned
catch
else
namespace static_assert
using
char
enum
new
static_cast
virtual

char16_t
explicit
noexcept
struct
void
char32_t
export
nullptr
switch
volatile
class
extern
operator
template

wchar_t
const
false
private
this
while
constexpr
float
protected thread_local
const_cast for
public
throw

Funzione Main

Ogni programma ha una funzione principale chiamato main, 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_GRECO

Costanti 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

Cpp TipoDati.jpg

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';

string

String su Arduino

Stringa

string testo = "Bravo come sempre";

string testo2 = "Amico " + "mio.";

bool Tipo booleano che rappresenta "vero" o "falso"

bool test = true;

Enumeratori
Si usa il qualificatore enum
enum 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".
/\/\/\/\/\/\/\ DA REVISIONARE (ci sono inesattezze) /\/\/\/\/\/\/\
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
Per andare accapo si usa il carattere speciale \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(&timestamp);
 
  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 input
void 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.h anche 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::cout accede alla variabile cout nel namespace std.
  • Accesso a membri statici di classe: MyClass::myStaticMember accede 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)


C++ | Cpp Teoria


Arduino C++ | C# | Visual Studio | MS SQL | Dizionario IT | Dizionario


Parole chiave:

Author