Author Topic: C  (Read 5517 times)

0 Members and 1 Guest are viewing this topic.

Offline MsZ

  • Il Manutentore
  • Militante
  • ******
  • Posts: 913
  • GNUru Meditation
C
« on: Tue 04 March 2008, 14:39 »
Qua mi ci metto. Sto ri-studiando il C, ed il discorso è strettamente correlato con il mio recente progetto. GNU/Linux è scritto al 99.9999% in C, e per fare qualcosa di decente devo capire come funziona il sistema nelle fondamenta -anche solo a grandi linee.
Vorrei infilare qui dentro possibili trucchetti, impressioni e/o osservazioni sul codice. Anche solo descrizioni o ragionamenti.

Se qualcuno vuole partecipare, anche solo per correggermi o farmi notare degli errori, è ben accetto. :biggrin:
Comincerò entro breve -già stasera posterò qualcosa- ma adesso ho da fare. (Intanto ho aperto il topic -sto prenotato :asd: )
« Last Edit: Tue 04 March 2008, 21:31 by MsZ »

Offline MsZ

  • Il Manutentore
  • Militante
  • ******
  • Posts: 913
  • GNUru Meditation
Re: C
« Reply #1 on: Tue 04 March 2008, 22:24 »
Programmazione ordinata

Probabilmente questa è la base ideale da cui cominciare.

Un programma -immagino che lo sappiate tutti- è una sequenza ordinata di istruzioni. Le istruzioni si suddividono generalmente in tre tipi più una: sequenza, selezione e iterazione, oltre alle varie dichiarazioni che possono essere fatte. Dei quattro tipi quest'ultimo è l'unico ad essere non sempre necessario. Più avanti spiegherò perchè.

Sequenza

Una sequenza è un insieme lineare di istruzioni, più o meno complesse, messe in ordine per esecuzione. Si parte dall'inizio, si finisce alla fine. Mi sembra chiaro. Una sequenza può essere un intero programma o l'interno di una funzione. La macchina esegue automaticamente le istruzioni scritte nel programma, una dopo l'altra, dall'inizio alla fine.

Selezione

Una selezione può essere semplice o multipla. Una selezione semplice è una condizione che dà come risultato un valore booleano (vero o falso). Se la condizione è vera la selezione impone un percorso del programma. Altrimenti (ovvero se è falso quando si parla di valori booleani, che raggruppano l'intera cerchia dei valori "non veri") il programma segue un'altra strada.
Una selezione multipla mette in condizione di poter "scegliere" tra diverse possibilità messe a disposizione del programma scritto, siano esse immesse dall'utente o risultato di elaborazioni interne al programma stesso. Per ogni caso della selezione multipla il programma potrà percorrere strade differenti.

Iterazione

Un'iterazione è una singola istruzione od un gruppo di istruzioni, semplici o complesse, che vengono eseguite più di una volta, sotto determinate condizioni, dettate da specifici "parametri di controllo", elaborati dal programma stesso o forniti dall'utente.

Dichiarazioni

Dette anche "enunciazioni" o "inizializzazioni", sono quelle istruzioni che permettono al programma di identificare il valore delle variabili che si vanno ad utilizzare.
Alcuni linguaggi di programmazione come il C non prevedono una dichiarazione implicita di variabili. Se si vanno ad elaborare variabili non inizializzate, sotto determinate condizioni i risultati potranno essere del tutto casuali, in modo assolutamente letterale. Per la maggior parte dipende dal tipo di variabile. Per esempio, dichiarando implicitamente una variabile i senza inizializzarla ad alcun valore, un comando che poi ne visualizzi il contenuto potrà dare risultati apparentemente assurdi, sebbene la variabile sia stata dichiarata. Questo perchè il contenuto della porzione di memoria assegnata per quella variabile è ancora presente, e viene stampato senza alcun tipo di filtraggio.
Generalmente quindi è sempre una buona norma inizializzare le variabili appena vengono dichiarate, oppure farlo la prima volta che vengono utilizzate, ma solo in casi particolari.


« Last Edit: Thu 06 March 2008, 00:19 by MsZ »

Offline MsZ

  • Il Manutentore
  • Militante
  • ******
  • Posts: 913
  • GNUru Meditation
Re: C
« Reply #2 on: Thu 06 March 2008, 00:42 »
Dichiarazioni

Le dichiarazioni prevedono l'utilizzo di porzioni di memoria in cui immagazzinare valori chiamati variabili o (meno frequentemente) costanti. Esistono principalmente due tipi di dichiarazioni (più uno, che verrà discusso in seguito): dichiarazioni globali e locali.
Le variabili (o costanti) globali vengono dichiarate all'interno della funzione main () e sono porzioni di memoria che restano vincolate per tutta la durata del programma.
Le dichiarazioni locali possono essere inizializzate in una funzione, e rimarranno fissate all'interno di quella funzione. Appena la funzione cessa di essere eseguita la variabile (o la costante) cessa di esistere, la porzione di memoria a cui si riferiva viene rilasciata e può essere riutilizzata.
In poche parole, una variabile locale può essere dichiarata in un contesto in una funzione, e poi essere utilizzata in tutt'altra situazione in un'altra funzione, senza alcuna interferenza esterna. A meno che non venga esportata, ma questo aspetto del codice verrà esplorato più avanti.

Le variabili

Le variabili, come già accennato, sono piccole porzioni di memoria temporanea utilizzata per depositare un valore, numerico o alfanumerico, che verrà poi utilizzato all'interno del programma o della funzione, con esiti differenti.
Una dichiarazione come

int c = 20;

prende una porzione di memoria libera per il compilatore, la contrassegna con il nome "c" e ne assegna il valore numerico intero 20. Esistono molti tipi diversi di variabili. int è uno di questi. Altri tipi comuni sono char, float, long e double.
Alcuni tipi possono essere combinati per ottenere altri tipi di variabili, come long double o long int.
Le variabili possono essere con o senza segno. Nel caso di int, l'arco di valori che il tipo può supportare va da -2147483647 a 2147483648. E' possibile cambiare il campo di valori, ponendolo da 0 fino a 4294967296 con la parola chiave unsigned.

unsigned int c;

Ciò vale per tutti gli altri tipi di variabili con segno.

(Attualmente, esiste un limite numerico che dipende dall'architettura della macchina, tecnicamente. Ma con le macchine più recenti problemi di questo tipo non dovrebbero presentarsi. Eventualmente sappiate che l'errore di "Memory overflow" può dipendere anche da un'errata inizializzazione dei tipi di variabili più "corposi" come long double o long long. E comunque il compilatore avverte se un valore supera una certa soglia consentita, con un messaggio di warning o uscendo brutalmente. :biggrin: )

Le costanti

Le costanti, dal nome, non permettono alcuna modifica del valore interno. Vengono dichiarate aggiungendo la parola chiave const a sinistra del tipo della variabile dichiarata.

const int c = 20;

dichiara una costante di tipo intero con il nome c e la pone al valore numerico 20. Il valore rimarrà tale per tutto il programma o per tutta la funzione, nel caso sa una costante locale. Per tutte le altre caratteristiche valgono le regole già spiegate per le variabili.


« Last Edit: Thu 06 March 2008, 01:00 by MsZ »

Offline MsZ

  • Il Manutentore
  • Militante
  • ******
  • Posts: 913
  • GNUru Meditation
Re: C
« Reply #3 on: Thu 06 March 2008, 00:57 »
Sequenza

La sequenzialità è la caratteristica più lineare di un programma. Tutte le istruzioni vengono eseguite una dopo l'altra, dall'inizio alla fine, a meno che non sia detto di fare altrimenti, come nel caso di rimandi a funzioni, interruzioni di cicli, selezioni multiple o paragoni.

Inutile dire che cosa succederebbe se un semplice gruppo di azioni venisse eseguito non in ordine.

-Prendere le chiavi-
-Aprire la porta di casa-
-Aprire lo sportello della macchina-
-Salire in macchina-
-Chiudere lo sportello-
-Infilare le chiavi nella fessura-
-Accendere il motore-
-Ingranare la retromarcia-
-Uscire dal recinto di casa-
-Andare in strada-

Senza seguire l'ordine potrebbe essere possibile fare delle cose del genere?

-Salire in macchina-
-Aprire la porta di casa-
-Prendere le chiavi-
-Andare in strada-
-Uscire dal recinto di casa-
-Aprire lo sportello della macchina-
-Ingranare la retromarcia-
-Accendere il motore-
-Infilare le chiavi nella fessura-
-Chiudere lo sportello-

...io dico di no. Se poi qualcuno di voi ci riesce o è un mago o è al di sopra delle leggi della natura. Ma qui parliamo di programmazione e non di magia. ::)

Ovviamente la sequenzialità è importante. Senza sequenzialità un programma non può produrre i risultati che noi vogliamo che produca. Ma è importante anche il dettaglio con il quale queste azioni vengono compiute. Una macchina non può capire una frase come

Prendi la macchina ed esci in strada.

Questa azione complessa deve essere "spezzettata" in molte azioni più semplici, che una macchina sia in grado di "capire" ed eseguire. Per questo è importante sapere sempre con esattezza che cosa vogliamo far fare alla macchina, quando e dove. Altrimenti non solo la sequenzialità, ma l'intero programma potrebbe essere compromesso. Già soltanto la frase "prendi la macchina" potrebbe essere mal interpretata da un computer.
"Prendere" in che senso? Con le mani? Con un gancio, una gru? Come? Da che parte, su che lato? Per poi metterla dove?

Allo stesso tempo una certa azione ("prendere") implica altre azioni. "Prendere" una cosa implica uno strumento di presa. Implica un movimento di spostamento e una destinazione, una velocità di spostamento, rotazione... Tutte cose che devono essere precisate. Altrimenti l'azione stessa è incompiuta, o mal eseguita, ed il risultato (una macchina scassata perchè la gru ha mollato la presa a mezz'aria) potrebbe non sempre... essere apprezzabile. :happy:

Quindi, beh... sequenzialità è la seconda cosa importante, quanto la dichiarazione degli elementi che entrano in gioco. Più altre due: controlli e cicli (o iterazioni).


« Last Edit: Thu 06 March 2008, 01:00 by MsZ »

Offline MsZ

  • Il Manutentore
  • Militante
  • ******
  • Posts: 913
  • GNUru Meditation
Re: C
« Reply #4 on: Thu 06 March 2008, 12:22 »
Controlli

Io li chiamo "controlli". Ma sarebbe forse più appropriato chiamarli "confronti" o "scelte". Tuttavia "controlli" mi sembra più generale.

I controlli si suddividono in due categorie principali: confronti logici e scelte.

I confronti logici si hanno quando un valore (numerico o alfanumerico) viene preso e confrontato con un altro valore dello stesso tipo. Le scelte possibili sono due: se la condizione si verifica o se la condizione non si verifica. A seconda del risultato il percorso del programma potrà essere differente.
Un esempio classico è il confronto tra due valori numerici per stabilire se uno sia maggiore dell'altro.

if ( a > b )
then <istruzione>
else <istruzione>


Mette il programma davanti ad un bivio. Se a è maggiore di b viene eseguita l'istruzione (semplice o complessa) che segue la parola chiave then, altrimenti viene eseguito il corpo di comandi subito dopo l'else, se esiste.
<istruzione> può essere una singola istruzione od un gruppo di istruzioni. Qualora l'istruzione sia più di una che riguarda then o else sono obbligatorie le parentesi graffe, come per delimitare una funzione.

if ( a > b )
then {
...
...
}
else {
...
...
}


La posizione delle parentesi graffe è irrilevante. Per il compilatore spazi (qualora non siano sintatticamente richiesti), newline e tabulazioni non hanno significato. L'esempio sopra vuole soltanto essere il più chiaro possibile per un eventuale osservatore esterno. Basta che le parentesi racchiudano il corpo delle istruzioni nel modo inteso.

Le scelte (molto spesso chiamate anche "scelte multiple") pongono una condizione che può assumere diversi significati, non soltanto "vero" o "falso" come l'if. In questo caso il valore in esame viene confrontato con diversi valori forniti come scelta. Se c'è un riscontro il programma esegue l'istruzione o il gruppo di istruzioni relative. Se non c'è si può scegliere quale azione far fare alla macchina tramite parole chiave particolari.
Ovviamente si può utilizzare un comando di scelta multipla al posto di un if per indirizzare due sole scelte, ma è consigliabile sempre utilizzare le istruzioni giuste al posto giusto, salvo casi speciali o accorgimenti particolari.

Il modo più comune di implementare le scelte multiple è attraverso la funzione switch:

switch (a) {
   case 1:... ; break;
   case 2:... ; break;
   ...
   default:... ; break;
}


(I valori a destra di case sono puramente arbitrari. E' stata presa in considerazione una variabile numerica intera positiva con il nome di a.)

E' necessario fare alcune osservazioni sul comando switch.
1-Il corpo di switch deve sempre essere delimitato da parentesi graffe;
2-Ogni condizione posta da case (tranne nel caso in cui più condizioni facciano riferimento al medesimo percorso) deve finire con la parola chiave break;
3-Come già accennato, più condizioni possono essere riferite allo stesso corpo istruzioni;
4-Non sono necessarie parentesi graffe per delimitare più istruzioni per una singola riga di case.

break è importante in questo caso. Se non ci fosse, nel caso di una iterazione si avrebbe immediatamente un ulteriore confronto del valore in esame, che potrebbe portare a risultati inattesi. Per cui, normalmente un confronto deve avvenire una sola volta, tranne casi particolari. Dopo il primo confronto il programma esce dalla selezione multipla di switch e continua con l'istruzione immediatamente successiva. break è una parola chiave separata ed indipendente dalla funzione switch, ed è universalmente usata per interrompere la scelta multipla nel caso di singolo paragone.


« Last Edit: Fri 07 March 2008, 15:08 by MsZ »

Offline MsZ

  • Il Manutentore
  • Militante
  • ******
  • Posts: 913
  • GNUru Meditation
Re: C
« Reply #5 on: Fri 07 March 2008, 15:08 »
Come sempre, se vi sembra che abbia fatto qualche errore, correggetemi :biggrin:

Offline MsZ

  • Il Manutentore
  • Militante
  • ******
  • Posts: 913
  • GNUru Meditation
Re: C
« Reply #6 on: Sun 09 March 2008, 13:07 »
Iterazioni

Un'iterazione è un blocco di istruzioni o di funzioni che viene ripetuto finchè si verifica (o non si verifica) una determinata condizione, definita dall'iterazione stessa.

Esistono due tipi di iterazioni: iterazioni definite (di cui si conosce il limite di ripetizioni) e iterazioni indefinite (il cui numero di iterazioni è arbitrario o sconosciuto). Le iterazioni definite sono controllate da un valore definito contatore. Normalmente un contatore viene inizializzato ad un numero preciso (che può essere 0 o un numero diverso da zero) che viene poi aumentato o diminuito, a seconda della necessità, di una o più unità alla volta.


a = 1;
while ( a <= 10 ) {
   ...
   ...
   a = a + 1;
}

Esempio di ciclo definito. Il contatore è inizializzato a 1 e il ciclo si ripete finchè a non raggiunge 10; a viene aumentato di 1 ogni ripetizione.

Le iterazioni indefinite sono comunque controllate, ma in modi diversi. Un metodo molto usato è quello di chiedere un valore chiamato sentinella: quando viene inserito in input il programma ne verifica l'identità ed esce dal ciclo.

while ( a != -1 ) {
   ...
   ...
}

Iterazione indefinita. In questo caso a è il valore sentinella: se è diverso da -1 il ciclo continua. Se l'utente inserisce -1 in input il confronto viene fatto e il programma esce dal ciclo.

Parole chiave di controllo per iterazioni sono while, until e for.
L'uso di while è abbastanza semplice.

while ( condizione ) {
   (corpo del while)
}

Se la condizione si verifica il programma continua il ciclo all'interno del corpo del while. Non appena la condizione non è più verificata il programma esce dal ciclo.

until ( condizione ) {
   (corpo dell'until)
}

L'utilizzo dell' until è essenzialmente l'inverso del while. Il ciclo continua finchè la condizione dell'until non si verifica. Quando la condizione si verifica il programma esce dal ciclo.

Una iterazione until è equivalente ad una iterazione while con condizione negata:
while ( ! ( condizione) ) {
   (corpo del while)
}

L'operatore unario '!' è operatore di negazione. Messo davanti ad una condizione ne nega il risultato del confronto logico. Gli operatori verranno approfonditi in seguito.

Iterazione con for:

for ( expr1; expr2; expr3 ) {
   (corpo del for)
}


Il ciclo con for si avvale di tre espressioni. expr1 inizializza le variabili di controllo del ciclo per l'inizio del ciclo stesso; expr2 definisce il limite (o i limiti) per cui il ciclo deve continuare a lavorare; expr3 identifica l'avanzamento (o gli avanzamenti) delle variabili in gioco. E' come se fosse un while con "avanzamento e inizializzazione incorporati".
Ogni espressione può avere più termini di controllo, ma è preferibile fare in modo che una sola variabile faccia parte delle espressioni del for.

for ( i = 1; i <= 10; i++, a = a + 2 ) {
   (corpo del for)
}

Ciclo for semplice. i viene inizializzato a uno per il primo dei 10 cicli del for ( i <=10 ). Ad ogni iterazione i verrà aumentato di 1 ( i++ ) e a di due ( a = a + 2 ). I punti e virgola separano le espressioni principali del for; all'interno di ogni espressione (expr) più condizioni possono essere poste, separate da virgole (i++, a = a + 2).

Alcune espressioni possono essere omesse. Ad esempio è possibile creare un ciclo for teoricamente infinito omettendo la condizione di fine ciclo:

for ( i = 0; ; i++ ) {
   (corpo del for)
}

lasciando semplicemente uno spazio vuoto al posto dell'espressione che si vuole omettere. I punti e virgola vanno sempre scritti.

Allo stesso modo è possibile creare un ciclo for infinito senza alcuna espressione di controllo.

for ( ; ; ) {
   (corpo del for)
}

Questo genera un'iterazione indefinita semplice, non dipendente da alcun parametro. Il programma entra nel ciclo e continua a iterare finchè non si verifica una condizione particolare di interruzione tramite break (già visto con la scelta multipla nel caso di switch). Se la condizione con break non è presente all'interno del ciclo l'unico modo per interrompere il ciclo è interrompere il programma.


Break e continue
break e continue sono parole chiave particolari che gestiscono iterazioni. Tramite break è possibile interrompere un'iterazione, continuando l'esecuzione da dopo la fine del ciclo così come è scritto in un programma. Ogni altra iterazione verrà ignorata.
continue permette di saltare l'iterazione corrente, passando a quella successiva. In poche parole, alla presenza di continue il ciclo verrà continuato direttamente, ignorando ogni altra istruzione presente sotto la chiamata a continue. Un po' come lanciare un dado e terminare su una casella che permette di tornare al "Via!" saltando tutto quello che c'è tra la casella e il "Via!", passando direttamente al prossimo giro.

break e continue non hanno argomenti.

 

Creative Commons License All ValerioCipriani.com contents are published according to Creative Common License, except different instructions. The Staff is not responsible of eventually guide, article and publishing mistakes. All published items are patent free. All trade marks reported are right reserved. Contact us, Info.