• Non ci sono risultati.

Breve Ripasso del linguaggio C

N/A
N/A
Protected

Academic year: 2021

Condividi "Breve Ripasso del linguaggio C"

Copied!
39
0
0

Testo completo

(1)

Breve Ripasso del linguaggio C

Michelangelo Diligenti Ingegneria Informatica e

dell'Informazione

diligmic@diism.unisi.it

(2)

Compilazione, linking, ecc

editor

compiler

linker librerie

binario sorgente

sorgente

sorgente

sorgente

oggetto

oggetto

oggetto

oggetto .obj, .o .cpp, .cc, .c

(3)

Compilare

Se avete più sorgenti:

g++ -o nome_binario nome_programma1.cc ...

nome_programmaN.cc

Vedremo modi migliori per gestire progetti con un elevato numero di file sorgente

Attenzione: uno ed uno solo file sorgente deve contenere la funzione main

La funzione main fornisce il punto iniziale per l'esecuzione del programma

In caso di definizioni multiple il linker non saprebbe scegliere quale usare per far iniziare il programma

(4)

Funzioni

Le funzioni permettono di suddividere il codice in sottoparti modulari e combinabili

Riutilizzabili evitanto la ripetizione del codice

Permettono di contenere la dimensione degli eseguibili

Il compilatore crea un insieme di istruzioni a cui e' possibile saltare nel momento in cui la funzione e' chiamata

(5)

Funzioni e compilatore

istruzione 1 istruzione 2

Istruzione 3: Chiamata funzione

Flusso Programma

istruzione Funzione 1 istruzione Funzione 2 istruzione Funzione 3

...

...

Compilatore trasforma codice in sequenza di istruzioni in linguaggio macchina

Istruzioni sono eseguite in sequenza

(6)

Funzioni

istruzione 1 istruzione 2

Istruzione 3: Chiamata funzione

istruzione Funzione 1 istruzione Funzione 2 istruzione Funzione 3

...

...

Chiamata a funzione cambia flusso programma

Salto alle istruzioni che implementano la funzione

Terminata la funzione si risalta a dopo la chiamante

...

...

Funzione Salto (Jump)

(7)

Funzioni

Funzioni si definiscono ed usano come

tipo_ritornato NomeFunzione(tipo param1, …, tipo paramN);

Esempio

int Fact(int k) { int res = 1;

for (int i = 2; i <=k; ++i) res *= i;

return res;

}

int k = Fact(4);

(8)

Main

Main è una funzione speciale definita come:

int main(int argc, char** argv) { … codice … }

argc: numero argomenti passati

argv: argomenti passati. argv[0] nome del programma

#include <stdlib.h>

#include <stdio.h>

int main(int argc, char** argv) { int k = atoi(argv[1]);

printf(”%d\n”, k);

return 0;

}

Librerie

Funzioni di libreria

Valore ritornato: 0 vuol dire tutto OK

(9)

Main e funzioni

Esercizio

Compilate il programma precedente ed eseguitelo

Cosa succede se non si passano argomenti dalla linea di comando?

Correggete il programma in modo che funzioni anche se non sono passati argomenti

Aggiungete al programma la funzione per calcolare il fattoriale e ritornate il numero fattoriale

dell'argomento

(10)

Puntatori e references

& prende indirizzo di una variabile

* dereferenzia un indirizzo (puntatore) e prende il contenuto della cella di memoria puntata

Esempi

int i = 3; valore di i = 3

int *pi = &i; pi uguale ad indirizzo di i (punta ad i)

int &ri = i; ri riferisce all'indirizzo di i

ri = 4; cambio valore alla cella riferita da ri, cosa succede al valore di i?

*pi = 5; dereferenzio pi, e prendo il valore della cella e lo pongo pari a 5.

Cosa succede ad i?

(11)

Passaggio di parametri

Chiamante

Funzione chiamata void Func(int i)

Se func modifica i, cambia una sua copia non k stesso

int k

Func(k) int k

Func(k)

i copia separata di k

(12)

Passaggio di parametri

Chiamante

Funzione chiamata void Func(int i)

Se func modifica i, cambia una sua copia non k stesso

int k

Func(k) int k

Func(k)

i copia separata di k

(13)

Passaggio di parametri

Chiamante

Funzione chiamata void Func(int i)

Quando func esce la copia viene

distrutta int k

Func(k)

i copia di k

(14)

Passaggio di parametri

Chiamante

Funzione chiamata void Func(int* i)

int puntato da k

Adesso anche i punta alla stessa cella di k.

Se func modifica il contenuto di i, modifica anche il contenuto di k!

int k

Func(&k)

i* copia del

puntatore

&k

(15)

Passaggio di parametri

Chiamante

Funzione chiamata void Func(int* i)

Quando la funzione esce la copia viene eliminata

Ma la modifica della cella

puntata resta!

int puntato da k

int* k Func(k)

i copia del

puntatore k

(16)

Passaggio di parametri

Tre modi di passare i parametri, per valore, per indirizzo o per riferimento

Passaggio per valore

void Function(int i); // passo il valore di i, che viene

// copiato e reso disponibile nella funzione

ATTENZIONE: copia di i distrutta uscendo da Function

#include <stdio.h>

void Function(int i) { i = 5; } int main() {

int i = 0;

Function(i);

printf(”%d\n”, i);

}

(17)

Passaggio di parametri

Passaggio per indirizzo

void Function(int* i); // passo l'indirizzo di i, il puntatore eliminato uscendo da Function (ma non la cella puntata)

#include <stdio.h>

void Function(int* i) { *i = 5; } int main() {

int i = 0;

Function(&i);

printf(”%d\n”, i);

}

(18)

Passaggio di parametri

Passaggio per riferimento

void Function(int &i); // passo il riferimento all'indirizzo di i

#include <stdio.h>

void function(int& i) { i = 5; } int main() {

int i = 0;

Function(i);

printf(”%d\n”, i);

}

(19)

Passaggio di parametri

Esempio

#include <stdio.h>

#include <sys/time.h>

#include <vector>

bool function1(std::vector<int> vec) { return (vec.size() == 0); }

int main() {

struct timeval starttime, endtime;

gettimeofday(&starttime, NULL);

std::vector<int> vec(1000000, 1);

for (int i = 0; i < 10000; ++i) function1(vec);

gettimeofday(&endtime, NULL);

long msec = 1000000*(endtime.tv_sec - starttime.tv_sec) + (endtime.tv_usec - starttime.tv_usec);

printf("%ld\n", msec);

}

Dato grande 4*1000000 bytes, lo copio 10000 volte chiamando la funzione

Passo il vettore per valore

Misuro il tempo in microsecondi

(20)

Passaggio di parametri

Esempio, con passaggio per indirizzo

#include <stdio.h>

#include <sys/time.h>

#include <vector>

bool function2(std::vector<int>* vec) { return (vec->size() == 0); }

int main() {

struct timeval starttime, endtime;

gettimeofday(&starttime, NULL);

std::vector<int> vec(1000000, 1);

for (int i = 0; i < 10000; ++i) function2(&vec);

gettimeofday(&endtime, NULL);

long msec = 1000000*(endtime.tv_sec - starttime.tv_sec) + (endtime.tv_usec - starttime.tv_usec);

printf("%ld\n", msec);

}

Copio 10000 volte 8 bytes Chiamando la funzione

Ora Copio solo il puntatore (8 bytes)

Misuro il tempo in microsecondi

(21)

Passaggio di parametri

Quando si passa un parametro, si fa una copia dello stesso

In genere questo non costa molto: per tipi come int, float, long in, ecc. Si tratta di 4-8 byte.

Ma questo può essere costoso. Ad esempio, se un

tipo è composto da 4*106 byte

(22)

Ritornare i risultati

Fare attenzione a come si ritornano i risultati di una funzione

Stesse attenzioni del passaggio di parametri

(23)

Ritornare i risultati per valore

Per valore: il dato ritornato viene copiato in nuova variabile

Esempio

int function() { int i = 0;

return i;

}

int j = function(); // CORRETTO: contenuto di i // copiato in j e poi i viene distrutto

Il valore copiato resta disponibile fuori

(24)

Ritornare i risultati

Per indirizzo, si ritorna l'indirizzo della variabile

indirizzo viene copiato e ritornato

#include <stdio.h>

int* function() {

int* i = new int; // puntatore i copiato in j e poi eliminato in return i; // uscita ma la cella resta allocata

}

int main() {

int* j = function(); // OK!

printf(”%d\n”, *j)

delete j; // ricordarsi di deallocare }

(25)

Ritornare i risultati

Per riferimento, fare molta attenzione int& function() {

int i = 0;

return i;

}

int& j = function(); // NO! i viene distrutto per cui il // riferimento non valido fuori

Ritornare per riferimento sembra poco utile adesso,

ma lo rivedremo quando studieremo il concetto di

classe

(26)

Ritornare i risultati

Per indirizzo o riferimento. Attenzione!

int* function() { int i = 9; return &i; }

int* j = function(); // attenzione i viene deallocato all'uscita della funzione, j punta ad una cella

deallocata!!!

int& function() { int i = 9; return i; }

int& j = function(); // anche qui riferimento verso a cella deallocata!

(27)

Ritornare i risultati

Per indirizzo

Attenzione di nuovo...

int* function() { int i = 0; return &i; } Come chiamo la funzione?

int* j = function(); // NO! i viene eliminato all'uscita.

// j punta ad una cella di memoria

// non più allocata

(28)

Ritornare i risultati

Alternativa: passare un puntatore alla funzione

Specifica dove la funzione mi scrive l'output

Esempio

void function(int* i) { *i = 0;

}

int i = 5;

function(&i);

printf(“%d”, i);

(29)

Ritornare i risultati

Una funzione deve fornirmi un risultato complesso

Alternativa 1

struct Point {int x; int y; };

Point* function() {

Point* p = new Point;

p->x = 0; p->y = 0;

return p;

}

Point* p = function(); … // do something with p

delete p;

(30)

Ritornare i risultati

Alternativa 2

struct Point {int x; int y; };

void function(Point* p) { p->x = 0; p->y = 0; };

Point p;

function(&p);

Alternativa 2 va quasi sempre preferita

Non si deve allocare dinamicamente

si evitano bugs di memoria o leaks perché si scorda di deallocare

Chiarezza e leggerezza sintattica

(31)

Ritornare i risultati

Esercizio: scrivere un programma che

usa la struct per rappresentare un Punto data in precedenza

contiene una funzione che assegna alle coordinate del punto dei valori passati come argomenti

la funzione ritorna il punto usando l'alternativa 2

il Main legge le coordinate (x,y) da linea di comando, chiama la funzione e stampa le coordinate del punto sullo schermo

(32)

Constness

Previene che variabile possa essere modificata

Utile per prevenire cambiamenti ad una variabile a livello di compilazione.

Talvolta si cambia accidentalmente una variabile e si creano bugs difficilmente trovabili

Const garantisce che tali bug sono trovati in compilazione

void Function (int &i) {

i = 5; // credo di fare un'operazione locale ma invece cambio all'esterno }

void Function (const int &i) {

i = 5; // Errore in compilazione, i non può essere cambiata }

(33)

Constness

void Function (const int *i) { int j;

*i = 5; // errore in compilazione, il puntatore è const i = &j; // si può fare

}

void Function (const int * const i) { int j;

*i = 5; // Errore in compilazione i = &j; // Errore in compilazione }

(34)

Constness

void Function (const int i) {

i = 5; // Errore in compilazione

int* j = &i; // Errore in compilazione const int* j = &i; // OK

const int k = 3;

k = 5; // Errore }

Non solo per i parametri.

Lo si può usare per qualsiasi variabile

(35)

Passaggio di parametri e const

Esempio, con passaggio per riferimento

#include <stdio.h>

#include <sys/time.h>

#include <vector>

bool function2(const std::vector<int>& vec) { return (vec.size() == 0); }

int main() {

struct timeval starttime, endtime;

gettimeofday(&starttime, NULL);

std::vector<int> vec(1000000, 1);

for (int i = 0; i < 10000; ++i) function2(vec);

gettimeofday(&endtime, NULL);

long msec = 1000000*(endtime.tv_sec - starttime.tv_sec) + (endtime.tv_usec - starttime.tv_usec);

printf("%ld\n", msec);

}

Copio 10000 volte 8 bytes Chiamando la funzione

Ora Copio solo il puntatore (8 bytes)

Misuro il tempo in microsecondi

(36)

Passaggio di parametri e const

Il C/C++ permette di passare i parametri in molti modi simili (es. Riferimento o puntatore)

Consiglio di usare solo 2 modi per tipi complessi

Passaggio per riferimento const per parametri che non devo poter essere cambiati

Passaggio per indirizzo per parametri che devono essere cambiati dalla funzione

Vantaggi di questo stile:

Massima velocita visto che tutto è passato per indirizzo

Chiarezza per il chiamante di cosa potenzialmente cambia

Uso consistente del const che evita bugs

(37)

Passaggio di parametri e const

Esempio (seguirò questo stile in seguito)

Funzione

p1, p2 dati passati che sono non modificabili

variabile center viene modificata dalla funzione

struct Point {int x; int y; };

void Center(const Point& p1, const Point& p2, Point* center);

Finite l'esercizio a casa...

(38)

Consigli di stile (IMPORTANTE)

Spezza linee lunghe in più righe che stanno sullo schermo (mio consiglio max 80 caratteri per riga)

Codice rimane leggibile se lavorate su un laptop

One definizione/comando per riga

Usa le maiuscole per i nomi di tipo (struct Point) o di funzioni (void Center(…))

Usa le minuscole per i nomi delle variabili

Non usare nomi brevi, ma nomi che spiegano

immediatamente cosa fa una funzione o variabile

(39)

Consigli di stile (IMPORTANTE)

Usa indentazione in modo consistente

Usa linee bianche per spezzare il codice in unità semantiche

Commenta il codice in modo appropriato

Sulle slides spesso non posso seguire lo stile per

mancanza di spazio, ma voi FATELO sempre

Riferimenti

Documenti correlati

Enunciare il teorema di Weierstrass (esistenza di massimi e minimi di funzioni continue) e spiegare come si possono trovare i punti di massimo e di minimo.. Determinare, se esistono,

(a) Enunciare il teorema di Fermat (relativo alla derivata di una funzione nei punti di massimo o minimo).. Enunciare, in modo completo e preciso, il teorema

(2) Si e’ enunciata e dimostrata la prima parte del secondo teorema fondamentale del calcolo integrale ( cfr. Par.9 Funzioni integrali, Th.6.10 punto 1, p.307 e Note finali ).. (2)

della funzione F, gli intervalli in cui e’ crescente o decrescente e i suoi even- tuali punti di massimo o minimo locali, gli intervalli sui quali ha concavita’.. rivolta verso l’alto

[1] Dare la definizione di limite di funzione nel caso lim x→1 f (x) = −∞, scrivendo esplicitamente gli intorni.. [2] Dare la definizione di funzione continua e di funzione

©Paola Gervasio (UniBS) - Analisi Matematica 1 Punti di discontinuit` a

Si enunci in generale la regola di derivazione della funzione composta e se ne fornisca un esempio di applicazione..

[r]