C TUTORIAL (parte II)
BAKUNIN
bakunin@meganmail.com
Con enorme piacere nuovamente ritorno!
Questa guida, se vogliamo dargli un nome, potremmo definirla:
CORSO PSEUDO-AVANZATO
INDICE
- Test
- Occorrente
- Teoria
- a) Lettere consentite
- b) Spazi bianchi
- c) Parole chiave
- d) Commenti
- e) Costanti
- Aritmetica
- If e ?
- Switch e case
- Funzioni
- Errori comuni e frequenti
- Puntatori
- Redirezione
- #define & soci
- Tra Windozz e Linux
1. TEST
Ovviamente non tutti possono accedere a questo corso, quindi farò un piccolo test.
Se capite alla perfezione questo piccolo programmino in C, allora siete a cavallo, altrimenti ritornate alla lezione 1.
--------INIZIO--------------
#include <stdio.h>
main(){
int risposta;
int numero1;
int numero2;
int risp;
char enter;
inizio:
printf("\n\r CALCOLATRICE v1.0\n\r");
printf("\n\r\r\rQuesto semplice programmino permette di:\n\n\r");
printf("1) Sommare due numeri\n\r2) Sottrarre due numeri\n\r");
printf("3) Moltiplicare due numeri\n\r4) Dividere due numeri \n\r5) Uscire");
printf("\n\n\rCosa vuoi fare?");
scanf("%d",&risposta);
if (risposta<6){
if (risposta<5){
if (risposta<4){
if (risposta<3){
if (risposta<2){
if (risposta<1){
printf("\n\n\n\n\n\n\rMI STAI PRENDENDO PER 'U CULO??\n\n\n\n\n\n\r");
goto inizio;
}
else{
printf("\n\n\rDammi il primo dei due numeri: ");
scanf("%d",&numero1);
printf("\n\n\rDammi il secondo dei due numeri: ");
scanf("%d",&numero2);
risp=numero1+numero2;
printf("\n\n\n\n\n\n\rIl risultato %d\n\n\n\n\n\n\r",risp);
}
}
else{
printf("\n\n\rDammi il primo dei due numeri: ");
scanf("%d",&numero1);
printf("\n\n\rDammi il secondo dei due numeri: ");
scanf("%d",&numero2);
risp=numero1-numero2;
printf("\n\n\n\n\n\n\rIl risultato %d\n\n\n\n\n\n\r",risp);}
}
else{
printf("\n\n\rDammi il primo dei due numeri: ");
scanf("%d",&numero1);
printf("\n\n\rDammi il secondo dei due numeri: ");
scanf("%d",&numero2);
risp=numero1*numero2;
printf("\n\n\n\n\n\n\rIl risultato %d\n\n\n\n\n\n\r",risp);
}
}
else{
printf("\n\n\rDammi il primo dei due numeri: ");
scanf("%d",&numero1);
printf("\n\n\rDammi il secondo dei due numeri: ");
scanf("%d",&numero2);
risp=numero1/numero2;
printf("\n\n\n\n\n\n\rIl risultato %d\n\n\n\n\n\n\r",risp);
}
}
else{
printf("\n\n\n\n\n\n\rCIAO!!\n\n\n\n\n\n\r");
goto fine;
}
}
else{
printf("\n\n\n\n\n\n\rMi stai prendendo per 'U culo??\n\n\n\n\n\n\r");
}
goto inizio;
fine:
}
----------FINE--------------
Non vi preoccupate: di sostanza c'è poco! E' tutto grafica. Avete capito qualcosa? Spero di sì!
2. OCCORRENTE
Sotto volontà di Screener_it, mi sono sbattuto per voi e ho trovato un compilatore di C per Windozz. Questo vi farà molto contenti! Il compilatore si chiama lcc e lo potete scaricare nella sezione programmi del Tank Commandos
In tutto non sarà superiore a i 2 MB.
3. TEORIA
Lo so! La teoria è sempre la parte più noiosa, ma se volete creare programmi funzionanti e di notevole spessore è necessario avere ben presente i limiti e le possibilità reali. Con il C si può fare di tutto (basta pensare che Linux è scritto praticamente tutto in C!), quindi, all'opera!
Lettere consentite.
Il C non capisce tutte le lettere che io posso inserire. Ne comprende solo un certo tipo.
Queste sono:
- dalla a alla z
- dalla A alla Z
- da 0 a 9-
alcuni segni speciali:
-------------------------------------------
| , | / | - |
| . | \ | = |
| ; | ~ (alt+126) | + |
| : | ? | <> |
| ' | % | () |
| " | & | {} (alt+123; alt+125) |
| ! | ^ | [] |
| | | * | # |
-------------------------------------------
Questi sono gli unici segni che il C capisce!
Spazio bianco.
Ogni volta che io faccio più di due spazi, una tabulazione, un a capo, il C decodifica come uno spazio solo. Se io quindi scrivo:
a b
oppure
a
b
oppure
a b
Il C capirà sempre: a b !
Parole chiave.
Non si possono usare tutte le parole che ci passano in mente come variabili o puntatori. Certe parole non possono essere utilizzate poichè richiamano determinate funzioni. E' comunque logico nel senso che non posso fare una variabile col nome printf perché con lo stesso nome io indico il comando di scrittura.
Comunque eccovi un'altra tabella:
-------------------------------------------
| auto | else | int | struct |
| break | double | long | switch |
| case | extern | register | typedef |
| char | float | return | union |
| continue | for | short | unsigned |
| default | goto | sizeof | while |
| do | if | static | void |
-------------------------------------------
Ovviamente poi ci sono i comandi specifici di ogni libreria (vedi scorsa lezione!). Non c'è quindi printf nell'elenco perché questo comando fa parte dei comandi della libreria stdio.h .Ovviamente non vi metto l'elenco di tutti i comandi di ogni singola libreria, ma vi metto solo quei comandi che girano senza bisogno di inizializzare nessuna libreria: Quelli della tabella sopra.
Commenti.
I commenti si fanno così:
/*Questo è un commento!!*/
Questo già lo sapete. Forse però non sapete che è incredibilmente errore fare un commento dentro l'altro! Il programma legge in malo modo e parte.
Esiste anche un comando (che io personalmente non ho mai usato) che permette di eliminare un intera procedura se questa contiene anche solo un commento. Ciò si fa così:
#ifdef KNOCKIT
ciao(){/*TANTO VA LA GATTA AL LARGO CHE AFFOGA!*/}
#endif
Con il comando #ifdef KNOCKIT chiuso con #endif si elimina tutta la procedura ciao se questa contiene commenti.
Costanti.
Le costanti come dice il termine stesso sono un qualcosa che rimane costante per tutto il programma e che io grazie a ciò posso richiamare più facilmente. Queste costanti possono essere numeriche intere, di un carattere o di una stringa. Quelle numeriche intere possono essere in base 10, in base 8 o in base 16. Per dire che è una costante in base 10 basta scriverla come tale. Per la base 8 bisogna metterci uno 0 davanti, mentre per la base 16 basta mettere uno 0x o 0X davanti.
Non si possono mettere spazi! Ogni costante numerica viene convertita automaticamente in long.
Quelle costanti di carattere devono essere contenute fra apostrofi.
Quelle di stringhe devono essere in uno di questi 3 tipi:
char ciao 1 []="bakunin"; Contenendo tutta la parola.
char ciao 2 []={'b','a','k','u','n','i','n',\0}; Dividendo tutti il lettere e
considerandola come insieme di
costanti di carattere.
char cuai 3 [8]={'b','a','k','u','n','i','n',\0} Uguale a prima ma specificando il
numero delle lettere.
Come potete vedere negli ultimi 2 casi, la costante finisce con \0 che è il comando fine
variabile o costante. Nulla di che, basta ricordarsene.
Per quanto riguardo poi l'uso, bisogna ricordarsi che è necessario o usare il comando static, il quale serve per specificare esattamente che si tratta di costanti statiche, oppure legare le costanti a puntatori.
Detto sotto forma di esempio:
static char ciao 4 []="bakunin";
oppure
char *ciao 5="bakunin";
4. ARITMETICA
Incremento decremento
Come gia' accennato nella lezione precedente si usa = per attribuire un valore ad una variabile. Penso poi che sia intuitivo l'uso di + - * e / (le 4 operazioni). Forse non è del tutto chiaro che con il C si può incrementare una variabile anticipando o posticipando alla variabile stessa ++. Per decrementarla si usa ovviamente --.
Ad esempio:
int x,z=2;
x=(++z)-1;
A questo punto x=2 e z=3
int x,z=2;
x=(z++)-1;
A questo punto x=1 e z=3
Cosa notiamo? Nel primo esempio io inizializzo 2 variabili x e z. Impongo che x valga quanto z aumentata di 1 e sottratta di 1. Ovviamente z resterà aumentata di 1.
Nel secondo invece io dopo aver inizializzato dice che x è uguale a z a 2 sottratto di uno e poi aumento z. Dov'è la differenza? Che nel primo caso io metto ++ prima e quindi impongo che l'incremento avvenga prima del calcolo della x. Nel secondo prima calcolo la x e poi aumento la z.
Divisioni.
Se io dico che x è un integer (cioè inizializzo x attraverso il comando int x), se faccio questa operazione x=3/2 il risultato sarà 1.
Come faccio ad avere il giusto risultato?
Ci sono 2 strade:
- Uso al posto che int float
- Scrivo x=3.0/2.0
In entrambi i modi avrò il corretto risultato.
Uguaglianza, disuguaglianza, maggiore o minore di.
Per dire x uguale a 3?, non posso certo dire x=3 perché farei diventare x veramente uguale a 3. Per fare ciò si usa il comando ==
Se devo fare un if devo fare:
if (x==3) (operazione())
Al contrario se diversità, uso il comando !=. Quindi:
if (x!=3) (Operazione())
Ovviamente valgono < o >.
if (x>3) ...
if (x<3) ...
Il C, come forse voi non sapete può non solo lavorare ad alto livello (quindi giocare con le variabili già fatte), ma può anche lavorare a basso livello (ciò vuol dire giocare con i byte). Cosa serve se il lavoro è già fatto? E se un giorno qualcuno di voi vuole crearsi un windozz 2002 per i cavoli propri a casa propria, come fa? Deve lavorare a basso livello.
Il basso livello è molto utile per programmi che giocano sotto altri. Quelli invisibili come virus o simili.
Comunque dopo questa interessante digressione parliamo di.... byte.
Gli operatori di bitwise (che operano sui singoli bit) sono i seguenti:
- "&" AND
- "|" OR
- "^" XOR
- "~" Complemento a 1 (0=>1, 1=>0)
- "<<" shift a sinistra
- ">>" shift a destra
Questi comandi servono per gestire i bit e non vanno confusi con gli && e i || per quanto riguarda i cicli. Sono molto diversi!
~ e' un operatore unario, cioe' opera su un solo argomento indicato a destra dell'operatore. Lo vedremo dopo.
I comandi shift muovono i bit in una specifica direzione. La variabile va messa verso destra.
<< Sposta tutto verso sx mentre >> verso dx.
ES:
z<<2
shifta i bit in z di due posti verso sinistra
ottengo così z=00000010 (binario) o 2 (decimale)
se poi dico
z>>=2
ottengo z=00000000 (binario) o 0 (decimale)
E' di fondamentale importanza capire il concetto di binario e decimale. Le informazioni che il computer muove a velocità impressionanti ogni secondo sono in base 2 o in base 16. Bisogna ricordarselo bene. Quindi non spaventatevi se vi dico che 10 in base 2 è uguale a 2 in base 10 come non spaventatevi dei numeri come 00000010 perché il computer sposta informazioni a pacchetti di 8 bit. Indi percui si usa mettere 8 numeri sempre sia pure per la maggior parte 0.
Forse non sono stato molto chiaro con l'esempio di prima.
Quando dicevo z>>=2 porta a z=00000000 (binario) o 0 (decimale) intendevo dire che con shift a sinistra ottengo una moltiplicazione e con a destra una divisione.
Se ritorniamo all'inizio dell'esempio con z<<=2 ottengo z=00001000 (binario) o 8 (decimale) perché 4 per 2 arrivo a 8. Questa è matematica.
Comunque non vi preoccupate se state impazzendo o se non state capendo nulla. E' praticamente ovvio. Questo è l'ultimo porto del C mentre noi ne abbiamo ancora tanti da fare assieme.
Ci ritornerò su questo argomento, non vi preoccupate.
Operazioni composte.
E' necessario fare attenzione al significato di un'espressione come
a + b * c
Per noi è quasi scontato che prima avvengono le moltiplicazioni e poi le somme (lo sapete vero?). Per il questo non è proprio così. Infatti noi potremmo ottenere un risultato come:
(a + b) * c
oppure come:
a + (b * c)
Il C sceglie in base alla priorità degli operatori. Questa può essere alta o bassa (vedi
sopra alta e bassa!). Quando gli operatori sono assolutamente uguali, come nel nostro caso ove a, b e c sono uguali come variabili e locazione nella memoria, il computer li legge da sinistra verso destra. Quindi legge:
(a + b) * c
Bisogna quindi precisare bene! Ciò si fa con l'uso delle parentesi.
5. IF E ?
Per le operazioni base dell'IF, tipo:
if a() b() else c()
si può usare anche un'altra formula. Questa è quella del ?.
Si usa così e semplifica molti passaggi.
a() ? b() : c()
Semplice semplice:
(x==3) ? printf("Lo è!!"):prinft("NON LO è!");
6. SWITCH E CASE
Questi due comandi servono per giocare con più scelte.
ESEMPIO:
switch (letter) {
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
numerovocali++;
break;
case " ":
numerospazi++;
break;
default:
numerocostanti++;
break;
}
Io prima devo inizializzare la variabile letter poi devo farla prendere dal computer con il comando scanf, e dopo di che, parte questo esempio. Questo dice:
- incomincia lo studio delle possibilità di letter. (switch)
- I vari casi della variabile (da A a U).
- Incremento la variabile numerovocali++ di 1 se la lettera è una vocale.
- Il comando break (già detto nell'altra lezione) esce dal ciclo.
- Se è uno spazio aumenta il numero di spazi;
- Con il comando default si dice che tutte le altre possibilità vanno contate nella variabile numero costanti.
7. FUNZIONI
Una cosa che non ho detto nell'altra guida:
int ciao() si usa solo quando ipotizzo che ci deve essere un ritorno numero dalla funzione ciao. Ciao deve svolgere dei calcoli e poi a me serve il risultato nella funzione principale detta main(). Se ciò non mi serve (raro!), cioè creo una funzione che fa operazioni varie ma che poi dopo non ridia nessun risultato alla funzione centrale bisogna usare:
void ciao()
Così quando la chiamo, ciao parte ma è totalmente inutile!
8. ERRORI COMUNI E FREQUENTI
Questi sono gli errori che io facevo (e a volte faccio ancora) durante la programmazione!
- Attenzione a = che è diverso da ==
- Tutte le funzioni devono avere anche se vuote ()
- Tutti gli array devono avere []
- Le maiuscole e le minuscole sono diverse per il C.
- i ;
9. PUNTATORI
Qui la vedo tesa...
perché un puntatore? Un puntatore si usa perché così rendo più flessibile un programma. Lo rendo più adattabile a un sistema operativo piuttosto che ad un'altro. In più, con l'uso dei puntatori posso fare operazioni che altrimenti sarebbero letteralmente impossibili! E' ovvio il fatto che sono difficili da capire. Sono difficili da utilizzare, ma una volta entrati nell'ottica, il gioco è fatto.
Cos'è un puntatore? Un puntatore è un tipo di variabile che ha una esatta collocazione nella memoria. Questa variabile può contenere un'altra variabile o più.
Il puntatore può essere di qualsiasi tipo.
Per crearlo bisogna usare il comando:
int *puntatore
(ovviamente int può essere sostituito col tipo di variabile che noi vogliamo!)
Con il segno & io fornisco l'indirizzo di una variabile (vedi col comando scanf) mentre con il comando * io do il contenuto.
Ora per semplificare il tutto vi beccate un esempio:
-----------INIZIO--------------
int *puntatore;
int x=1,y=2;
puntatore=&x; /*passaggio 1*/
y=*puntatore; /*passaggio 2*/
x=puntatore; /*passaggio 3*/
*puntatore=3; /*passaggio 4*/
-------------FINE--------------
Spiego:
Per capire cosa succede devo guardare come gestisce la macchina il tutto.
Supponiamo che la variabile x sia locata nello spazio di memoria 100 e quella y nella 200, il puntatore invece nella 1000.
Con il passaggio 1 io faccio in modo che il puntatore si posizioni nello spazio di memoria 100, cioè quello della x. Con il secondo faccio in modo che la variabile y assumi il valore del puntatore, che, essendo collegato a x diventa 1. Quindi y vale 1. Con il terzo passaggio faccio assumere alla variabile x il valore del puntatore che è 100. Con l'ultimo passaggio, il 4 dico che il contenuto del puntatore è 3.
Possiamo quindi dire che i puntatori hanno tre tipi di informazioni:
puntatore che mi indica il valore, il contenuto della variabile (considero il puntatore
come una qualsiasi variabile)
&puntatore che è l'indirizzo fisico della variabile all'interno della memoria
*puntatore che mi indica il contenuto della locazione della memoria.
Un puntatore deve sempre essere allocato in una determinata zona della memoria. QUESTO è IMPORTANTISSIMO!!! Se così fate il programma casca. Si blocca il sistema!
Quindi questo è un errore ultra grave!
int *errore;
*errore=100;
Mentre è giusto fare:
int *giusto;
int x;
giusto=&x;
*giusto=100;
EX CLARO?
Di per se esiste anche un altro metodo per non dover fare così, ma finisce con l'essere più lungo. Questo si basa sulla libreria malloc() e va usata così:
int *ciao;
ciao=(int *) malloc(100);
A me sembra più lungo! Comunque è assolutamente uguale.
Dato che i puntatori sono come delle variabili, io posso fare tranquillamente tutto
ciò che posso fare con le variabili. Indi per cui, io posso fare un po' di aritmetica:
float *a, *b;
*a=*a+10;
++*a;
(*a)++;
a=b;
I puntatori, le funzioni, le strutture e gli array.
I puntatori e le funzioni sono in stretto legame. Questo accade soprattutto il trasferimento di dati da una funzione ad un altra con dati diversi. Io non posso preventiva che tipo di dato mi arriva (se provo a inserire un char in una variabile int, non credo sia facile!) uso un puntatore. Lui ha la sua memoria. Gli butto dentro ciò che voglio e mi risolvo il problema.
Posso anche usare strutture di variabili come posso usare array di puntatori. Il principio è comune!
Tutto quanto è uguale alle variabili!
10. REDIREZIONE
Chi ha pratica con il DOS o ancora di più con UNIX e LINUX sa già queste cose:
---------------------------------------------------------------------------
| > | stdout (standard output) Inserisce l'esecuzione di un programma |
| | in un file particolare da noi scelto. |
| < | stdin (standard input) Inserisce dentro un programma ciò |
| | che è scritto in un file. |
| | | Prende l'output da un programma e lo immette come input in un altro.|
---------------------------------------------------------------------------
Tutto ciò a noi cosa ci serve? Ci serve con il mitico comando getchar() putchar(). Queste due librerie si usano così:
int getchar(void);
int putchar(char ch);
Il primo comando legge un dall stdin un char, mentre il secondo scrive un char dal un stdout.
Per quanto riguarda ciò bisogna poi ricollegarci a printf, scanf, fscanf, fprintf.
Ora vi spiego anche altri due comandi collegati a ciò: Sscanf e Sprintf. Questi scrivono e leggono da una stringa precedentemente assegnata:
int x=10;
char=messaggio[80];
sprint(messaggio,"il valore di x è %d",x);
Sempre in questo argomento vi inserisco l'uso delle opzioni nell'esecuzioni. Avete in mente quei programmi che per partire hanno bisogno di opzioni (sto parlando di DOS o UNIX!)? ESEMPIO:
pkzip c:\prova.zip *.*
Bene queste opzioni facoltative, nel nostro caso c:\prova.zip *.*, si possono richiedere elencandole all'interno delle parentesi di main().
Quindi al posto di mettere main() metteremo main(int argc,char ++argv).
Queste sono le uniche operazioni, comandi che si possono inserire tra le parentesi di main!
Spiegazione:
argc è il nome del programma in questione.
argv è anche il nome del programma in questione più tutte le sue opzioni.
Ora l'esempio chiarificatore:
----------INIZIO-------------
#include<stdio.h>
main(int argc,char ++argv){
int i;
printf("argc=%d\n",argc);
for(i=0;i<argc;++i)
printf("argv[%d]: = %s\n",i,argv[1]);
}
---------FINE----------------
Questo semplice programmino (che dovreste capire al volo!) serve a stampare le opzioni del programma eseguito.
Se salvo il programma come CIAO, scrivendo: CIAO mamma,papà,zio,nonno
io avrò:
argc=5
argv[0]=CIAO
argv[1]=mamma
argv[2]=papà
argv[3]=zio
argv[5]=nonno
Dovete quindi notare che argv 0 è il nome del programma!
11. #DEFINE
Il programma #define serve per creare macro o mini programmini dentro un programma. Serve quindi a sveltire il lavoro, a renderlo più maneggevole.
Il modo da utilizzare è:
#define <macro> <operazione>
Quindi potrebbe essere un esempio questo:
#define max(A,B) ((A)>(B)?(A):(B))
Ve lo ricordate che ? sostituisce l'IF?
Posso fare anche programmi più grossi:
#DEFINE begin{
...operazioni...
#UNDEFINE end}
Questa è la struttura da utilizzare.
Poi c'è l'ormai stra-famoso #include
ESEMPIO
#include<stdio.h>
Vi dice qualcosa? Non ve lo rispiego e vado oltre.
#if serve per creare degli if con i #
Esempio
#ifdev CIAOMAMMA
#include<stdio.h>
#else CIAOMAMMA
#include<io.h>
#endif CIAOMAMMA
L'esempio mi sembra già abbastanza chiarificatore. Bisogna mettere un nome e richiamarlo sia per else e sia per endif.
Per questa lezione direi che è tutto!
12. Tra Windozz e Linux
Ma prima di salutarvi vi voglio dire ancora una cosa:
Il C è nato sotto UNIX e quindi sotto LINUX. Per imparare ad usare C e C++ (ma anche lo java, l'assembler,...) windows non è il massimo! Direi anzi che è la cosa più squallida.
Qualche dato e qualche data:
Se vi dicessi che per programmare in DOS non ci sono problemi (programmini stupidi)voi lo sapete. Ma se vi dicessi di programmare una finestra di WINDOZZ?
Lo sapete che la microsoft fa pagare a suon di milioni le sue librerie per C?
Lo sapete che fa pagare manuali a suon di 100, 200 mila lire?
Lo sapete che questo è un furto?
Linux da tutte le librerie gratuite complete di manuali. Potete creare finestre più belle e colorate che WINDOZZ e tutto ciò gratuito!
Poi a voi la scelta! Prezzo dei programmi base per windowzz (win 98, office 97 e balle varie): 1.000.000
Prezzo di Linux Mandrake (versione uscita una settimana fa) con programmi vari
(StarOffice che sarebbe Office per windozz, LinZip, Giochi, programmi vari di tutti i tipi): 9.000
E non vi sto prendendo in giro!!
Ci si vede!
BAKUNIN
bakunin@meganmail.com