Monthly Archives: January 2015

Altro giro altra corsa… FreeBSD 10.1

Prima di riprendere con la stesura di post relativi alla programmazione in C stanotte mi sto
cimentando in una installazione minimale dell’ultima versione stabile del sistema operativo
Unix-like FreeBSD, la 10.1.
Anche in questo caso spero di scrivere qualcosa in merito a detto sistema operativo e
alle sue peculiarita’ e funzionalita’ con particolare riferimento alla loro applicazione
in contesti “enterprise”.

La prima cosa che salta’ all’occhio delle modifiche apportate a quest’ultima release e’
sicuramente la presenza di un menu per la selezione del kernel nel boot loader seguito dallo stabile ed affidabile FreeBSD Installer che oltre all’estrema semplicita’ supporta ora l’installazione sul filesystem ZFS (anche encrypted all’occorrenza).
Vedremo nel corso delle prossime “puntate” l’effettivo set di caratteristiche e funzionalita’ che
l’OS mette a disposizione. Tra tutte bhyve, jail, carp, hast e service saranno sicuramente quelle alle quali dedichero’ maggior tempo anche in funzione di un confronto con quanto reso disponibile da Linux seguite poi dal nuovo strumento di gestione dei package, l’immensa collezione di ports e l’esecuzione di FreeBSD 10.1 come control and management domain in Xen grazie alla nuova tipologia di guest PVH.

‘night!

[PR0GRAMMAZi0NE UNiX: rIPASSO C][ PARTE IV : Struttura programma C ]


----------------------[ PR0GRAMMAZi0NE UNiX: rIPASSO C ]----------------------
---------------------[ PARTE IV : Struttura programma C ]---------------------
--------------------------------[ abnm3nt4l1 ]--------------------------------

Indice

00. Introduzione
01. Consumo
03. Costanti e variabili
04. Espressioni in C
05. Istruzioni e blocchi di istruzioni in C
06. Funzioni in C

00. iNtrODUZIONE
------------

Bene dopo aver superato piu' o meno indenni il periodo compreso tra il 24
Dicembre 2014 e il 2 Gennaio 2015 (piu' o meno perche' ~ 4 kg di peso
guadagnati per uno che oltre all'informatica ha la passione per la corsa
a piedi sono TAANTA ROBA!) vi do' un augurio di un anno ricco di
serenita', felicita', prosperita' e soddisfazioni. So che la serenita'
non e' una caratteristica delle giornate di un VUA ma augurarcela non
costa nulla (per il momento!).
Sull'altra mia passione prima o poi conto di pubblicare qualcosina
in futuro. Per ora torniamo all'informatica e in particolare al C.
In questo episodio daremo un'occhiata alla struttura di un programma
scritto in C.
Fino a ora abbiamo visto che un programma scritto in C dev'essere compilato
per essere eseguito e cosa accade dietro le quinte quando utilizziamo il
compilatore GCC per convertire il nostro sorgente in eseguibile binario.
Ora diamo un'occhiata alle peculiarita' del linguaggio e della struttura
di un programma. In particolare faro' un escursus su:

- Costanti e variabili;
- Espressioni;
- Istruzioni;
- Blocchi di istruzioni;
- Tipi di funzioni e nomi in C;
- Passaggio di argomenti alle funzioni;
- Il corpo delle funzioni;
- Chiamate di funzione;

02. cONSUMO
-------

1) un'arancia del Medio Campidano
2) 30 gr di frutta secca non salata
3) te' verde non zuccherato

03. c0St4nt1 e vARiaBIli
--------------------
Cosi' come un edificio e' costruito da mattoni, un programma in C e' si compone
di elementi base come espressioni, istruzioni, blocchi di istruzione e funzioni.
Prima pero' di discuttere questi elementi di base occorre prendere dimestichezza
con due costituenti dei programmi C, piccoli ma importanti, sui quali si basano
le espressioni: costanti e variabili.

Come si evince dallo stesso nome con il termine costante ci si riferisce a valori
fissi che non vengono alterati nel corso del programma. Questi valori sono chiamati
anche "literals" (leterali).
Le costanti possono essere di qualsiasi tipo fondamentale del C: numeri interi,
decimali, caratteri, stringhe e perfino enumerazioni.
Le costanti sono trattate alla stregua delle variabili eccetto per il fatto
che il loro valore non puo' essere alterato.

Vediamo come definire le costanti in C e quali errori vengono generati se si cerca
di modificarne il valore:

/* const.c */
#include

/* definizione di una costante con direttiva al preprocessore #define */

#define COSTANTE1 "CIAO\n"

int main (void) {
/* definizione di una costante tramite keyword const */
const char COSTANTE2[] = "CIAO CIAO\n";

printf("%s", COSTANTE1);
printf("%s", COSTANTE2);
return 0;
}

Questo codice compilato ed eseguito fornisce il seguente output.

> ./const
CIAO
CIAO CIAO

Vediamo le differenze tra le due modalita' di dichiarazione di valori costanti.

Utilizzando la direttiva al preprocessore in fase di inclusione questo cerchera'
le varie occorrenze della costante nel programma e le sostituira' con il valore.

In questo caso non si tratta comunque propriamente di una costante in quanto non
viene allocata memoria all'interno del programma e come detto in precedenza quando
il compilatore in fase di pre-processing incappa in una #define cerca le occorrenze
della costante nel programma e ne sostituisce il valore con quello dichiarato.

Output del preprocessing del nostro programma

-x-
int main (void) {
const char costante2[] = "CIAO CIAO\n";

printf("%s", "CIAO\n");
printf("%s", costante2);
}
-x-

Con la const viene invece creata una vera e propria variabile in sola lettura
in modo pulito e veloce. E' il metodo preferito per dichiarare variabili di tipo
costante.

Una variabile, contrariamente a cio' che abbiamo detto per le costanti, altro
non e' che un'area di storage per i dati del nostro programma.
I valori contenuti nell'area di storage referenziata da una variabile possono
variare nel corso dell'esecuzione del nostro programma.
Ogni variabile in C e' di un tipo specifico; questo determina:
la dimensione e il layout dell'area di memoria, il set e il range di valori
che possono essere immagazzinati e il tipo di operazioni che possono essere
effettuate sulla variabile.
Il nome puo' essere composto da lettere, numeri e caratteri underscore. Il
nome deve iniziare con una lettera o un underscore. Tenete inoltre a mente
che il C e' case-sensitive quindi distingue caratteri maiuscoli e minuscoli.
int pippo e int Pippo rappresentano due differenti nomi di variabili di tipo
intero.

In seguito vedremo i tipi di dato in C e le operazioni applicabili a questi.

04. Espressioni in C
----------------

Le espressioni sono la combinazione di costanti, variabili e operatori utilizzati
al fine di ottenere un particolare risultato.

Per esempio:

(3 * 5) + 10

e' un'espressione che moltiplica 3 a 5 e poi aggiunge 10 al risultato.
Esistono diversi tipi di espressione. Per esempio 6 e' una espressione costante;
z e' un'espressione variabile; 6 + z e' un'espressione di una costante a cui e'
sommata una variabile; exit(0) e' un'espressione di chiamata a funzione.

Oltre a numeri e operatori, le espressioni si compongono degli identificatori.
In questa famiglia riccadono sia i nomi di variabili e costanti che quelli di
funzioni e parole chiave del linguaggio.

05. iStRuz1oNi 3 bL0cCh1 di 1sTrUzion1 (sT4t3meNt) in C
---------------------------------------------------

Nel linguaggio C uno statement e' un'istruzione "completa" ovvero che si conclude
con un ";".
In molti casi e' possibile trasformare una espressione, vedi un'assegnazione di
variabile, in una istruzione aggiungendo un ";".

Es.:

z = 4;

Un gruppo di istruzioni puo' costiture un "blocco di istruzioni".
Un blocco di istruzioni (stament block) viene racchiuso tra parentesi graffe { ... }.

Per esempio quanto segue rappresenta il blocco di istruzioni della funzione main():

int main (void) {
const char costante2[] = "CIAO CIAO\n";

printf("%s", "CIAO\n");
printf("%s", costante2);
}

06. Funzioni in C
-------------

Le funzioni costituiscono i building block dei programmi in C. Oltre alle funzioni
standard e' possibile crearne e utilizzarne di tue all'interno di un programma C.
Nel primo programma che abbiamo scritto (e anche nei successivi esempi) abbiamo
fatto riferimento a funzioni standard del linguaggio o incluse nella libreria
standard: main(), return , printf().
Ora diamo un'occhiata da piu' vicino all'anatomia di una funzione in C.

La definizione di una funzione in C ha la seguente forma:

tipo_di_dato_restituito nome_funzione (lista dei parametri)
{
corpo della funzione
}

Una funzione in C consta di un header e di un corpo. Vediamo nel seguito le
parti che compongono questi due macrocostituenti:

- tipo di dato restituito: una funzione puo' restituire un valore. Il tipo di funzione
indica il tipo di dato che questa restituisce al termine dell'esecuzione. Alcune
funzioni, meglio note come procedure non restituiscono alcun tipo di dato al
termine della loro esecuzione. Per queste il return type sara' void.
- nome_funzione: e' il nome che identifica la funzione. E' bene che sia un nome
mnemonico e che riconduca a cio' che la funzione esegue.
- parametri: ai quali ci si riferisce come argomenti indicano i dati in input
alla funzione. Sono del tutto opzionali. Vi sono funzioni che non
necessitano di parametri
- corpo della funzione: consta di un insieme di funzioni che determinano cio'
che fa la funzione.

Es.:

/* funzione massimo: restituisce il piu' grande tra due numeri interi */

int massimo (int numero1, int numero 2) {
int risultato;

if ( numero1 > numero2 ) risultato = numero1;
else risultato = numero2;
return risultato;
}

Ovviamente e' possibile separare la dichiarazione dalla definizione della funzione.
Per esempio questo e' utile quando si definisce la funzione in un file sorgente
diverso rispetto a quello nel quale la funzione e' dichiarata.
Per esempio se noi dobbiamo utilizzare (chiamare) la funzione massimo nel file
main.c ma questa e' definita nel file funzioni.c in main.c dovro'
dichiarare in main.c la funzione, prima che questa venga chiamata, come segue:

int massimo(int, int);

Nella dichiarazione non e' importante il nome dei parametri ma solo il loro tipo,
cosi' che il compilatore possa valutare la correttezza della chiamata.

Un altro esempio di separazione tra dichiarazione e definizione si ha quando
si vogliono raggruppare tutte le definizioni di funzioni alla fine del nostro
programma, dopo il main. In questo caso perche' queste possano essere utilizzate
nel corpo del nostro programma occorre definire i prototipi o sia le dichiarazioni
prima del main.

Es.:

#include

/* dichiarazione massimo */

int massimo(int, int);

int main ( void ) {

printf("Il massimo tra 20 e 21 e': %d\n", massimo(20,21));
return 0;
}

/* definizione di massimo */
int massimo(int numero1, int numero2) {
int risultato;
if (numero1 > numero2 ) risultato = numero1;
else risultato = numero2;
return risultato;
}

And now, vediamo come usare queste funzioni in C!

Making function call

Durante la creazione di una funzione in C si stabilisce cio' che essa
deve fare.
Quando invece un programma effettua una chiamata alla funzione questo,
passa il controllo alla funzione chiamata. Una funzione chiamata effettua
i task per i quali e' stata programmata e quando incontra il termina della
funzione "}" o l'istruzione di return, restituisce il controllo al chiamante.
Se e' programmata per restiture un valore di un tipo prestabilito lo fornira'
in output al chiamante.

Per chiamare una funzione occorre semplicemente passare i parametri che essa
si attende accanto al nome e qualora questa restituisca un valore memorizzarlo
da qualche parte.

Torniamo a massimo.

#include

/* dichiarazione massimo */

int massimo(int, int);

int main ( void ) {
int max ;

/* chiamata della funzione massimo */
max = massimo (2,3);
printf("il massimo tra 2 e 3 e': %d\n", max);
return 0;
}

/* definizione di massimo */
int massimo(int numero1, int numero2) {
int risultato;
if (numero1 > numero2 ) risultato = numero1;
else risultato = numero2;
return risultato;
}

Argomenti delle funzioni

Se una funzione deve usare degli argomenti devono essere dichiarate delle
variabili che accettino il valore per questi argomenti. Queste prendono
formalmente il nome di parametri.

Questi parametri vengono creati quando una funzione viene chiamata e distrutti
al termine della chiamata della funzione.

Quando chiamiamo una funzione ci sono due modi di passare gli argomenti:

per valore: questo metodo copia l'attuale valore di un argomento in un parametro
della funzione. In questo caso le modifiche apportate al parametro
all'interno della funzione non hanno effetto sugli argomenti.
per riferimento: questo metodo copia l'indirizzo dell'argomento nel parametro.
All'interno della funzione l'indirizzo e' utilizzato per accedere
all'argomento usato nella chiamata. Questo significa che le modifiche
apportate al parametro hanno poi effetti sull'argomento.

Esempio di funzione con passaggio di parametri per valore:

#include

void scambio ( int x, int z);

int main ( void ) {
int arg1, arg2 = 0;

arg1 = 2;
arg2 = 1;

printf("arg1 vale: %d - arg2 vale: %d\n", arg1, arg2);

scambio(arg1,arg2);
printf("Dopo scambio: arg1 vale: %d - arg2 vale: %d\n", arg1, arg2);

return 0;
}

void scambio( int x, int z) {
int temp;

printf("Funzione pre-scambio: parametro x vale: %d - parametro z vale: %d\n", x, z);

temp = x;
x = z;
z = temp;
printf("Funzione Scambiati: parametro x vale: %d - parametro z vale: %d\n", x, z);
return;
}

OUTPUT:

arg1 vale: 2 - arg2 vale: 1
Funzione pre-scambio: parametro x vale: 2 - parametro z vale: 1
Funzione Scambiati: parametro x vale: 1 - parametro z vale: 2
Dopo scambio: arg1 vale: 2 - arg2 vale: 1

Esempio di funzione con passaggio di parametri per riferimento:

#include

void scambio ( int *x, int *z);

int main ( void ) {
int arg1, arg2 = 0;

arg1 = 2;
arg2 = 1;

printf("arg1 vale: %d - arg2 vale: %d\n", arg1, arg2);

scambio(&arg1,&arg2);
printf("Dopo scambio: arg1 vale: %d - arg2 vale: %d\n", arg1, arg2);

return 0;
}

void scambio( int *x, int *z) {
int temp;

printf("Funzione pre-scambio: parametro x vale: %d - parametro z vale: %d\n", *x, *z);

temp = *x;
*x = *z;
*z = temp;
printf("Funzione Scambiati: parametro x vale: %d - parametro z vale: %d\n", *x, *z);
return;
}

OUTPUT:

arg1 vale: 2 - arg2 vale: 1
Funzione pre-scambio: parametro x vale: 2 - parametro z vale: 1
Funzione Scambiati: parametro x vale: 1 - parametro z vale: 2
Dopo scambio: arg1 vale: 1 - arg2 vale: 2

Enjoy,

-- abnm3nt4l1st