Total Hack Cheat
Benvenuto/a su Total hack Cheat....
non aspettate altro tempo Registratevi!!

Parte 3 guida linguaggio C

Vedere l'argomento precedente Vedere l'argomento seguente Andare in basso

Parte 3 guida linguaggio C

Messaggio Da RaYoZ il Gio Mag 20, 2010 12:14 pm

Funzioni di libreria del compilatore Microsoft

A questo punto abbiamo concluso la parte teorica sul linguaggio C anche se alcuni argomenti potremo riprenderli piu'avanti.
La teoria non e' che una piccola parte di quello che ci prefiggiamo di fare.
Solo un costante uso di cio' che abbiamo detto potra' servire ad ottenere risultati decenti.
In altre parole e' proprio vero che si impara sbagliando !
Dovremmo ora essere in grado di fare un qualche programma anche
non necessariamente semplice.
La potenzialita' del linguaggio C sta' anche nel fatto di conoscere le librerie di funzioni che abbiamo a disposizione con il compilatore che utilizziamo per poterle poterle applicare ai nostri programmi senza avere la necessita' di riscriverle. Nelle librerie del compilatore Microsoft C abbiamo funzioni per
gli scopi piu' svariati quali manipolazione di stringhe, ricerche, ordinamenti, controllo dei processi, funzioni matematiche e molte altre.
Per l'utilizzo di queste bisogna conoscere il modo con cui richiamarle e in particolar modo le dichiarazioni dei tipi degli argomenti a queste passati.
Piuttosto di una trattazione alfabetica ne preferisco una fatta per argomenti.
Molte di queste routine utilizzano macro, tipi e costanti dichiarati nei file di header (.h) che sara' nostro compito includere mediante la direttiva al preprocessore #include .
Se non specificato nessun tipo davanti alla funzione si intendono int.


Errori ritornati da funzioni

Molte funzioni della libreria del compilatore ritornano dei valori per avvertire se durante l'esecuzione di queste si sono verificati errori.
Il valore riportato dalla funzione non specifica il tipo di errore verificatosi.
Per poterlo testare esistono due metodi.
Il primo consiste nel dichiarare extern la variabile globale 'errno' e testarla nel caso che il valore ritornato segnali un irregolarita'.
Per ogni funzione che potrebbe ritornare un errore saranno riportate le costanti con cui sara' possibile eseguire il confronto con errno per conoscere la natura degli inconvenienti.
Il secondo metodo e' quello di utilizzare la funzione perror() che stampa sullo standard di errore il messaggio appropriato.
In perror() e' possibile anche specificare una stringa che verra' stampata con il messaggio d'errore stesso.

I valori degli errori sono definiti nel file di header errno.h e nel caso che si voglia seguire il primo metodo dovra' questo essere incluso nel nostro file.
Tenete a mente che errno in genere e' settato sull'ultimo errore verificato nell'esecuzione e che quindi il controllo e la stampa dell'errore dovra' essere fatta solo nel caso in cui si sia verificato effettivamente un errore.
In altre parole se voi mettete un istruzione di stampa errore incontrollata, questa potrebbe stampare un messaggio determinato da un'altra funzione eseguita precedentemente.
Una funzione che serve ad ovviare all'inconveniente e' clearerr che resetta l'indicatore di errore.


Funzioni per la classificazione e conversione di caratteri

Le seguenti funzioni eseguono dei test per verificare la natura del carattere passato come argomento.
Restituiscono un valore diverso da 0 se la condizione e' soddisfatta, 0 invece se no.
Un esempio di utilizzo

int var_2;
var = 'A';
var_2 = isalnum(var);


#include <ctype>

isalnum(var); /* Testa se var e' 'A'-'Z', 'a'-'z' o '0'-'9' */
isalpha(var); /* Testa se var e' 'A'-'Z' o 'a'-'z' */
isascii(var); /* Testa se var e' un carattere ascii 0-127 */
iscntrl(var); /* Testa se var e' un carattere di controllo */
isdigit(var); /* Testa se var e' cifra '0'-'9' */
isgraph(var); /* Testa se var e' un carattere stampabile 33-126*/
islower(var); /* Testa se var e' un carattere minuscolo */
isprint(var); /* Testa se var e' un carattere stampabile 32-126*/
ispunct(var); /* Testa se var e' un carattere di punteggiatura */
isspace(var); /* Testa se var e' un withespace 9-13 o 32 */
isupper(var); /* Testa se var e' un carattere maiuscolo */
isxdigit(var);/* Testa se var e' una cifra esadecimale */

int var;

Supponendo di dover eseguire un input controllando che i caratteri battuti siano o lettere o numeri potremo, tanto per portare un esempio pratico di utilizzo, scrivere una routine del
tipo:

int var_1, var_2;

for(;Wink
{
var_1 = getchar();
if((var_2 = isalnum(var_1)) != 0)
printf("%c ", var_1);

};


Le funzioni che abbiamo visto servono solo per il riconoscimento di certi caratteri.

Quelle che seguono servono invece alla loro conversione.


#include <ctype.h>

toascii(var); /* Converte var in un carattere ASCII */
tolower(var); /* Converte var in carattere minuscolo */
toupper(var); /* Converte var in carattere maiuscolo */
int var;

Chiaramente tolower e toupper svolgono il loro compito solo se il carattere passato come argomento e' approppriato e cioe' se ha un corrispondente maiuscolo o minuscolo.
A parte le innumerevoli applicazioni in cui possono essere utilizzate queste funzioni, un esempio potrebbe essere nel momento in cui vogliamo eseguire dei controlli su input di selezione da menu.
Il file che segue stampa un menu e inputa una scelta.

int var;
printf(" a ... Ordinamento file(s)\n");
printf(" b ... Stampa file(s)\n");
printf(" Scelta : ");
var = getchar();

A questo punto il nostro programma dovrebbe agire in base alla selezione fatta dall'utente.
Per vari motivi questo potrebbe inserire l'opzione desiderata si con un carattere maiuscolo che minuscolo.
Prevvedendo cio' dovremmo eseguire un doppio controllo
if(var == 'a' || var == 'A') ordina();

Potremmo anche, appena eseguito l'input, convertire mediante una delle precedenti funzioni il carattere inputato in modo da eseguire il controllo solo sui caratteri maiuscoli o minuscoli.

var = tolower(var);
if(var == 'a') ordina();

oppure potremmo usare un case

switch(var)
{
case 'a':
.........
}

invece di
switch(var)
{
case 'A':
case 'a':
.........

}



Conversione di data

Mentre le funzioni precedenti convertivano un carattere le seguenti eseguono conversioni di numeri in stringhe e viceversa.

#include <stdlib.h>
#include <math.h>

double atof(stringa); /* Converte stringa in un double */
atoi(stringa); /* Converte stringa in un int */
atol(stringa); /* Converte stringa in un long */
char *stringa;

Queste funzioni fermano la lettura della stringa da convertire al primo carattere non riconosciuto come una parte di un numero.
Le funzioni precedenti convertivano una stringa in un numero mentre quelle che vedremo ora fanno il contrario e cioe' convertono un numero in una stringa.

#include <stdlib.h>

char *itoa(var,stringa,base); /* Converte un int in stringa */
int var;
char *ltoa(var,stringa,base); /* Converte un long in stringa */
long var;
char *ultoa(var,stringa,base); /* Converte unsigned in stringa */
unsigned long var;



Queste dichiarazioni sono comuni a tutte e tre le funzioni precedenti.

char *stringa;
int base;

Un esempio :

int var_1;
char


Controllo delle directory

In alcuni casi si rivela molto utile disporre di funzioni adatte al controllo delle directory, ad esempio per poter sapere il path in cui ci troviamo con il programma, per poter creare una directory o cambiarla.
Piu' avanti vedremo, parlando della gestione degli interrupt, come crearci alcune funzioni simili senza supportarci su funzioni di libreria gia' esistenti.
La libreria del compilatore Microsoft ci mette a disposizione le seguenti funzioni.

#include <direct.h>

char *getcwd(stringa,n);
char *stringa;
int n;

Stringa e' l'array di char che conterra' il path riportato dalla funzione getcwd, mentre n e' il numero massimo di caratteri che puo' costituire il percorso.
Getcwd potrebbe riportare un errore testabile con errno settato sui seguenti valori :

ENOMEM - Non c'e' memoria sufficente per allocare n bytes ERANGE - Il path e' maggiore di n caratteri

Un esempio di utilizzo

char path_name[60];

getcwd(path_name,60);
printf("Corrente path : %s", path_name);

La funzione appena vista ci permetteva solo di venire a conoscenza del posizionamento in directory senza darci la possibilita' di cambiarlo o di modificarlo.
Vediamo ora tre funzioni che ci permettono di creare, cancellare e cambiare directory.

#include <direct.h>

chdir(stringa); /* Entra nel path specificato da stringa */
mkdir(stringa); /* Crea la directory specificata in stringa */
rmdir(stringa); /* Rimuove la directory specificata in stringa */
char *stringa;

Queste funzioni riportano come valore 0 se il lavoro ha avuto successo e -1 se no.
Un errore ricorrente facilmente anche sotto MS DOS e' quello di sbagliare a specificare il path della directory in cui si vuole entrare.
Il ritorno di -1 e' il mezzo con cui accorgerci se tutto e' andato per il verso giusto.
Esempio:

char path[50];
int var;

printf("Directory o <CR> per ritornare : ");
gets(path);
var = chdir(path);

Come dicevamo prima se il valore restituito e' -1 significa che c'e' stato un errore.
Le prime due funzioni, chdir e mkdir, potrebbero in questo caso settare il valore di errno su ENOENT che significa che il path non e' stato trovato.
Un ulteriore codice d'errore viene restituito con rmdir.
In questo caso potrebbe capitare, oltre a specificare un percorso inesistente, che il path corrisponda ad un file e non a una directory oppure che questa non sia vuota.
Errno sarebbe in questo caso settato su EACCES.


Funzioni che agiscono sui files

Molte di queste funzioni sono implementate dal compilatore Microsoft C per mantenere una compatibilita' software con il sistema operativo Unix anche se il lavoro da loro svolto in ambiente Dos non corrisponde effettivamente a quello dichiarato.
Ad esempio la funzione chmod dovrebbe cambiare i diritti di accesso ai files permettendo tutte le funzioni di lettura e scrittura o solo una di queste.
Difatto sotto Dos un file con permesso di sola scrittura sara' perfettamente leggibile in ugual modo.
Un file con permesso di sola lettura risultera' incancellabile.

Ad ogni modo vediamo le funzioni.

#include <sys\types.h>
#include <sys\stat.h>
#include <io.h> /* Richiesto solo per dichiarazione di
funzioni */

chmod(path,modo);
char *path;
int modo;

Modo e' uno dei seguenti valori

S_IWRITE Permesso di scrittura
S_IREAD Permesso di lettura

Questi possono essere uniti dall'operatore OR (|) per concedere
sia il permesso di lettura che quello di scrittura.

chmod("file.txt",S_IWRITE);

concede il permesso di scrittura sul file denominato file.txt.

chmod("file.txt",S_IWRITE|S_IREAD);

permette sia la lettura che la scrittura.

La funzione che ci permette di testare quali diritti abbiamo a disposizione su un file e' access.

#include <io.h>

access(path,modo);
char *path;
int modo;

L'intero modo puo' assumere, a seconda di quello che vogliamo testare, i seguenti valori

06 testa per permesso di scrittura e lettura
04 testa per permesso in lettura
02 testa per permesso in scrittura
00 testa solo l'esistenza del file

La funzione ritorna 0 se il file dispone del permesso specificato come argomento.
In caso negativo il valore restituito e' -1 e errno e' settato
sui seguenti valori dipendenti dai vari casi :

EACCES L'accesso e' negato. Il file non possiede i permessi specificati.


ENOENT Il path del file specificato non e' esatto. Il file non e' stato trovato.

Le funzioni appena viste permettevano di testare e di settare i permessi riguardanti i file gia' presenti nelle direttorie dei nostri dischi.
Sempre per compatibilita' con Unix il compilatore Microsoft riporta un ulteriore funzione che agisce sui permessi dei files creati con le funzioni creat, open e sopen dal processo corrente.

#include <sys\types.h>
#include <sys\stat.h>
#include <io.h> /* Richiesto solo per la dichiarazione di funzioni */

umask(modo);
int modo;

Modo e' una costante dichiarata in sys\stat.h che puo' assumere i seguenti valori.
Come nel caso della funzione chmod questi possono essere collegati mediante l'operatore OR (|).

S_IWRITE Permesso in scrittura
S_IREAD Permesso in lettura

Il seguente esempio concede ai file creati dal programma in esecuzione sia il permesso di lettura che di scrittura.

umask(S_IWRITE|S_IREAD);

Vediamo ora alcune funzioni che permettono di agire sul file per cambiargli il nome, cancellarlo e per conoscere la lunghezza di questo.

#include <io.h>

rename(nuovo_nome,vecchio_nome);
char *nuovo_nome;
char *vecchio_nome;

Rename cambia il nome di un file esistente.
Come per altre funzioni rename restituisce un valore pari a 0 se l'operazione ha avuto successo mentre -1 se si e' verificato uno dei seguenti errori testabili nella variabile errno o stampabili mediante perror().

EACCES Il nuovo nome del file esiste gia' o non puo' essere creato. Potrebbe anche dipendere dal fatto che il file specificato corrisponda ad una directory.

ENOENT Il file specificato non esiste

EXDEV Si e' cercato di muovere un file su un device differente.

Per cancellare un file e'possibile utilizzare la funzione unlink anche se ha la limitazione di poter cancellare un solo file per volta.
Vedremo parlando della funzione system() come sfruttare il comando interno Dos 'del' per cancellare piu' files
conteporaneamente.

#include <io.h> /* Richiesto solo per la dichiarazione delle
funzioni */

unlink(path);
char *path;

Anche in questo caso un valore di ritorno uguale a 0 indica la riuscita dell'operazione e -1 l'insuccesso.
I codici di errore testabili sono

EACCES Il path specificato corrisponde a una directory o a un file con solo permesso in lettura.

ENOENT Il file specificato non esiste.


Un esempio di utilizzo :

int var;

var = unlink("prova.txt");
if(var == -1)
perror("Non e' possibile rimuovere il file");


filelength e' la funzione che ci permette di sapere le dimensioni di un file.

#include <io.h> /* Richiesto solo per la dichiarazione delle funzioni */

long filelength(handle);
int handle;

L'argomento specificato in questo caso non e' il nome del file ma bensi' il numero di handle associato.
E' possibile avere il numero di handle mediante la funzione fileno() utilizzata specificando come argomento lo stream associato all'apertura di un file.
Prima di portare un esempio d'uso della funzione filelength vediamo l'utilizzo di fileno.

#include <stdio.h>

fileno(stream);
FILE *stream;

Esempio :

int var;
FILE *infile;
long lunghezza;

infile = fopen("data.txt","r");
var = fileno(infile);
lunghezza = filelength(var);

filelength riporta un valore uguale a -1L (long) se handle del file specificato non e' valido.
errno e' settato sul codice d'errore della costante EBADF.

La modifica delle dimensioni di un file e' resa possibile dalla funzione chsize che e' sempre riferita al handle di un certo file precedentemente aperto.

#include <io.h> /* Richiesto solo per la dichiarazione delle funzioni */

chsize(handle,dimensioni);
int handle;
long dimensioni;

chsize puo' estendere o ridurre le dimensioni di un file associato al handle specificato come argomento.
Nel caso che le dimensioni vengano ridotte il contenuto finale del file viene perso.
Il carattere \0 viene appeso invece nel caso che il file venga esteso come dimensioni.
Un valore di ritorno di -1 indica che si e' verificato un errore.
I valori testabili in errno per scoprire la natura di questo sono i seguenti :

EACCES Il file collegato al handle possiede solo permesso di lettura.

EBADF L'handle specificato non e' valido.

ENOSPC Nel caso si estenda il file significa che lo spazio a disposizione non e' sufficente.

Nel caso in cui sia necessario ottenere un maggior numero di informazioni riguardanti un determinato file o directory possiamo utilizzare le funzioni stat e fstat.
La prima si riferisce ad un path mentre la seconda utilizza come argomento l'handle di un file aperto precedentemente.
Vediamole singolarmente.

#include <sys\types.h>
#include <sys\stat.h>

stat(path, buffer);
char *path;
struct stat *buffer;

Nel caso di fstat abbiamo :

#include <sys\types.h>
#include <sys\stat.h>

fstat(handle, buffer);
int handle;
struct stat *buffer;

Le due funzioni riportano 0 se le informazioni sono state ottenute e -1 in caso contrario.
La variabile errno e' settata su EBADF ed indica un handle o path errato.
Come e' possibile notare tutte e due le funzioni utilizzano come argomento un puntatore ad una struttura di tipo stat dichiarata nel file di header stat.h in cui i membri conterranno i valori di ritorno.

st_mode Questo membro della struttura permette di identificare se l'handle o il path sono riferiti a un device a una directory o a un file.
E' anche possibile avere informazioni riguardanti i permessi abbinati.
Vedremo tra un po' il metodo di utilizzo.

st_dev Riporta il numero del drive del disco che contiene il file oppure l'handle nel caso di un device.

st_rdev Lo stesso di st_dev

st_nlink E' sempre 1.

st_size Riporta le dimensioni in bytes del file.
Chiaramente questo membro non e' settato se ci si riferisce ad un device.

st_atime
st_mtime
st_ctime Tutti e tre i membri riportano la data dell'ultima modifica che e' stata fatta al file.
Come nel caso di st_size i campi non sono a disposizione se ci si riferisce a un device.

All'interno dell'header stat.h sono definite anche le costanti che ci permettono di conoscere la natura dell'handle o del file a cui ci riferiamo.
Le costanti dichiarate sono le seguenti :

S_IFMT maschera per testare il tipo di file
S_IFDIR specifica una directory
S_IFCHR specifica un device
S_IFREG specifica un file normale
S_IREAD possiede permesso in lettura
S_IWRITE possiede permesso in scrittura
S_IEXEC possiede permesso di esecuzione

Per capire l'utilizzo della prima costante vediamo un esempio di utilizzo.

#include <sys\types.h>
#include <sys\stat.h>

struct stat buffer;
int var;

main()
{
char path[12];

printf("Specifica il nome del file o della directory : ");
gets(path);
var = stat(path,&buffer);
if(var == -1)
{
perror("Non posso avere informazioni del file");
exit(0);
}
if((buffer.st_mode & S_IFMT) == S_IFDIR)
{
chdir(path);
printf("Sono entrato nella dir %s", path);
exit(0);
}
if((buffer.st_mode & S_IFMT) == S_IFREG)
{
printf("Dimensioni del file : %ld", buffer.st_size);
exit(0);
}
if((buffer.st_mode & S_IFMT) == S_IFCHR)
{
printf("Si tratta di un device");
exit(0);
}
}


Per poter eseguire il confronto con le costanti che abbiamo visto bisogna eseguire un operazione di AND del membro st_mode con la maschera di tipo file S_IFMT.
Il programma precedente chiede in input un nome di file, directory o device ed esegue una stat.
Nel caso che risulti essere una directory ci entra dentro ed esce a sistema operativo.
Nel caso di un file riporta le dimensioni in bytes di questo e nel caso di un device ci avverte.

In uno dei capitoli precedenti, precisamente in quello in cui si parlava degli stream, avevamo accennato al modo di apertura di un file.
La differenza di aprire in modo testo o in modo binario un file si ripercuoteva sulla translazione dei caratteri CR/LF.
La seguente funzione permette di settare questo modo riferendosi al handle di un file o di un device.
Viene utilizzata tipicamente con stdout, stdprn, stderr ed stdaux.

#include <fcntl.h>
#include <io.h> /* Richiesto solo per la dichiarazione delle funzioni */

setmode(handle,modo);
int handle;
int modo;

Il modo puo' essere una delle seguenti costanti :

O_TEXT Setta il modo testo. Le sequenze CR/LF in input
vengono convertite in un singolo LF. In output il
carattere LF viene tradotto in una sequenza CR/LF.

O_BINARY Le translazioni precedenti vengono soppresse.

Il ritorno dalla funzione di un valore pari a -1 segnala un
errore.
errno e' settato su

EBADF Se l'handle del file non e' valido

EINVAL L'argomento che specifica il modo non e' valido.

Nelle versioni di Dos successive alla relase 3.00 e' possibile chiudere gli accessi in lettura e scrittura a un file durante l'esecuzione di un processo mediante la funzione locking().

#include <sys\locking.h>
#include <io.h> /* Richiesto solo per la dichiarazione
delle funzioni */

locking(handle,modo,num_byte);
int handle;
int mode;
long num_byte;

Il modo puo' valere una delle costanti qui riportate :

LK_LOCK Chiude il numero di bytes specificati. Se non possono essere chiusi riprova dopo un secondo.
Ripete il tentativo 10 volte prima di ritornare un errore.

LK_NBLCK Chiude il numero di bytes specificati. Se non ci riesce ritorna un errore.

LK_UNLCK Apre il numero di bytes specificato. I bytes devono essere stati precedentamente chiusi.


La funzione ritorna 0 se ha avuto successo.
Nel caso di -1 errno e' settato su uno dei seguenti valori :

EACCES In caso di tentativo di apertura significa che il file era gia' aperto. Il contrario in caso di tentativo di chiusura.

EBADF Handle non valido

EDEADLOCK Ritorna dopo il tentativo di 10 volte di chiudere un file con il modo LK_LOCK.

EINVAL share.com non installato.

Le versioni di Dos oltre la 3.00 possiedono un comando esterno chiamato share.com che altro non e' che un supporto per la condivisione dei file.
Per l'utilizzo di questa funzione e' necessario caricarlo precedentemente.
La funzione e' particolarmente utile in ambiente multiutente, ad
esempio per lo sviluppo di applicazioni per Multilink (tm).



Funzioni di I/O

Alcune di queste funzioni non ci risulteranno nuove in quanto ne avevamo gia' parlato precedentamente.

Ora la panoramica si espande trattando tutte le funzioni di I/O sia riferite a uno stream che ad un handle di un file.
Fondamentalmente a basso livello il C non possiede alcuna funzione di I/O bufferizzato.
A questo livello l' input/output viene eseguito dalle due funzioni read e write medianti le quali possono essere costruite delle funzioni come getchar e altre.
Vediamo il metodo di utilizzo di read e write.
int bytes_letti, bytes_scritti;
char buffer[512];

bytes_letti = read(des_fi,buffer,length);
bytes_scritti = write(des_fi,buffer,length);

dove des_fi e' il descrittore del file e length e' il numero di bytes da leggere o da scrivere.
Sia read che write riportano come valore il numero di bytes effettivamente letti o scritti.
Il descrittore di un file e' quello che abbiamo sempre chiamato handle e che viene ottenuto mediante l'apertura o la creazione di un file con routines a basso livello.
Quando abbiamo parlato di stream avevamo visto come aprire un file ed associarlo a una struttura di tipo FILE.
Le funzioni che seguiranno sono a basso livello e associano ai file o ai device specificati un handle.
Ricordo che stdin ha come handle 0, stdout 1 e stderr 2.
Nel caso che ad esempio si voglia utilizzare la funzione write con stdout bastera' specificare come descrittore file 1.
Alcune di queste sono praticamente uguali a quelle gia' viste solo che a differenza da quelle si riferiscono all'handle e non allo stream.

#include <sys\types.h>
#include <sys\stat.h>
#include <io.h> /* Richiesto solo per la dichiarazione
di funzioni */

creat(path,modo);
char *path;
int modo;

La funzione creat crea un nuovo file con il nome specificato in path oppure tronca il file esistente con quel nome a lunghezza 0.
Il ritorno di un valore pari a -1 indica un errore testabile come al solito in errno.

EACCES Il file specificato ha solo permesso di lettura oppure corrisponde ad una directory.

EMFILE Ci sono troppi files aperti e non c'e' a disposizione nessun handle.

RaYoZ
Admin
Admin

Messaggi : 1040
Punti : 2245
Data d'iscrizione : 03.04.10
Età : 22
Località : immerso nei pensieri

Tornare in alto Andare in basso

Vedere l'argomento precedente Vedere l'argomento seguente Tornare in alto

- Argomenti simili

 
Permesso di questo forum:
Non puoi rispondere agli argomenti in questo forum