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

Parte 2 (4) Guida linguaggio C

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

Parte 2 (4) Guida linguaggio C

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

ESC[#D (Cursor Backward)

Muove il cursore indietro di una o piu' colonne.

ESC[#;#R (Cursor Position Report)

Riporta attraverso lo standard d'input la corrente posizione del
cursore.

ESC[s (Save Cursor Position)

Salva la corrente posizione del cursore.

ESC[u (Restore Cursor Position)

Riprende la posizione del cursore precedentemente salvata.

ESC[2J

Cancella tutto lo schermo e mette il cursore in alto a sinistra.

ESC[K

Cancella dal cursore alla fine della linea

ESC[#;....;#m

Seleziona gli attributi dei caratteri specificati
Parametri:
0 Tutti gli attributi a Off
1 Chiaro ON (alta intensita')
4 Sottolineatura ON (solo per monocromatico)
5 Lampeggio ON
7 Reverse ON
8 Annullato ON (invisibile)

30 Foreground nero
31 " rosso
32 " verde
33 " giallo
34 " blu
35 " magenta
36 " azzurro
37 " bianco

40 Background nero
41 " rosso
42 " verde
43 " giallo
44 " blu
45 " magenta
46 " azzurro
47 " bianco



ESC[=#h o ESC[=h o ESC[=0h o ESC[?7h

Richiama lo schermo specificato dai parametri.

0 40 x 25 bianco e nero
1 40 x 25 colore
2 80 x 25 bianco e nero
3 80 x 25 colore
4 320 x 200 colore
5 320 x 200 bianco e nero
6 640 x 200 bianco e nero
7 Avvolge a fine linea

ESC[#;#;..#p o ESC["stringa"p o ESC[#;"stringa";#;#;"stringa";#p

Il valore iniziale definisce da quale codice ASCII inizia il
codice da mappare.
I rimanenti valori definiscono la sequenza dei codici ASCII generati quando questo tasto e' intercettato.


Esempi : ESC[65;81p A diventa Q
ESC[81;65p Q diventa A

Quest'ultima e' particolarmente utile nel momento in cui vogliamo ridefinire i tasti funzione. L'utilizzo di queste sequenze in un programma in linguaggio C pretende che il device ANSI.SYS venga installato nel file config.sys del disco da cui si fa' il boot del sistema.
Se questo non e' presente il programma lanciato stampera' a video letteralmente le sequenze di ESC senza che queste svolgano il loro compito di gestione video.
Un alternativa per la gestione del video e' quella di scrivere apposite funzioni utilizzando l'interrupt di gestione schermo.
In ogni caso per ora tralasciamo l'argomento per poi riprenderlo piu' avanti.
A questo punto dovremmo essere in grado di scrivere un programma
che anche esteticamente possieda buone caratteristiche.
Vediamo un esempio che racchiude un po' tutti i discorsi fatti fino ad ora.
Il programma esegue la copia di un file su un altro.
I nomi dei files sorgente e destinazione verranno richiesti da programma anche se, come vedremo tra breve, avremmo potuto passarli come argomenti alla funzione main specificandoli sulla linea di comando.
Nel caso che il file di origine non sia presente il programma esce avvisando dell'errore di file non trovato.


#include <stdio.h>

/* Definizione Macro per cancellare screen e posizionare curs. */

#define CLS puts("\33[2J")
#define AT(y,x) printf("\33[%d;%dH", y, x)
#define BELL putchar('\007')

main()
{
FILE *infile, *outfile; /* Streams files input e output */
char file_in[12]; /* File in input */
char file_ou[12]; /* File di output */
int carattere; /* Carattere letto da infile */

CLS; /* Cancella lo schermo */
AT(3,1); /* Posiziona il cursore */
printf("Source : "); /* Richiede nome file source */
gets(file_in); /* Inputa il nome in file_in */
AT(5,1); /* Posiziona il cursore */
printf("Target : "); /* Richiede nome file target */
gets(file_ou); /* Inputa il nome in file_ou */

/* Apre in lettura file_in e se non esiste ..... */

if((infile=fopen(file_in,"r")) == NULL)
{
BELL; /* Emette uno squillo */
AT(7,1); /* Posiziona il cursore */
/* Stampa che file_in non e' stato */
/* trovato ed esce a DOS */

printf("ERRORE: file %s non trovato", file_in);
exit(0);
}

outfile=fopen(file_ou,"w"); /* Apre file_ou in scrittura */

/* Finche' il carattere letto da infile e' diverso da EOF */
/* lo scrive su outfile */

while((carattere=getc(infile)) != EOF)
putc(carattere,outfile);

/* Uscito dal ciclo chiude i files aperti */

fcloseall();
}

Per i nostalgici delle comodita' di linguaggi quali il Basic e il Turbo Pascal dotati di comandi utili alla gestione del video quali locate, gotoxy ecc. il problema non e' grave in quanto la possibilita' di includere nei nostri programmi dei file esterni ci permette di scrivere le nostre macro di gestione in uno di questi e di utilizzarlo con qualsiasi programma che faremo.
Se nel programma precedente avessimo avuto all'interno del file
screen.h le definizioni di macro nel seguente modo

screen.h

#define CLS puts("\33[2J")
#define AT(y,x) printf("\33[%d;%dH", y, x)
#define BELL putchar('\007')

avremmo potuto, senza doverle riscrivere, includere il file nel nostro programma

#include <stdio.h>
#include "screen.h"



Passaggio di argomenti al main

Parlando delle funzioni avevamo visto che era possibile passare dei valori da una ad un altra.
Anche la funzione main ha la possibilita' di ricevere degli argomenti passati, mediante la linea di comando, dall'esterno.
Questa possibilita' avremmo potuto sfruttarla nel programma precedente per passare i nomi dei file di sorgente e di destinazione, anche se utilizzando questo metodo il controllo non avrebbe solo riguardato la presenza del file source ma anche il numero degli argomenti specificati.
In questo caso la funzione main viene richiamata con due argomenti per convenzione chiamati argc e argv.
Argc e' il numero degli argomenti passati compreso il nome stesso del programma mentre argv e' un puntatore ad un array di stringhe di caratteri che contiene i nomi degli argomenti.
Per portare un esempio supponiamo che il nostro programma si chiami FCOPY e che debba copiare il file DATA.TXT in DATA2.TXT.
La chiamata del programma sara'

fcopy data.txt data2.txt

In questo caso argc vale tre in quanto fcopy e' l'argomento 1 contenuto in argv[0], data.txt il 2 contenuto in argv[1] e data2.txt il 3 contenuto in argv[2]. (Ricordatevi che in C gli array e le matrici partono come indice da 0)Fate anche attenzione che nelle versioni del compilatore
precedenti alla 3.00 argv[0] e' la stringa "C".
In ogni caso, anche se non specificato alcun parametro, argc varra' sempre 1 in quanto lo stesso nome del programma da lanciare viene considerato un argomento.

Il nostro programma di copia si sarebbe dovuto scrivere nel
seguente modo:

main(argc,argv)
int argc;
char *argv[];
{
............
............

if(argc < 3)
{
BELL;
puts("ERRORE: Numero parametri errati");
exit(0);
}
if((infile=fopen(argv[1],"r")) == NULL)
{
................
................
}
outfile=fopen(argv[2],"w");

................
................
}



Puntatori

E' finalmente giunto il momento di approfondire il concetto di puntatori dei quali abbiamo gia' accennato qualche cosa nei capitoli precedenti.
Parlando degli operatori avevamo detto che & riporta l'indirizzo di una variabile.
Ad esempio

int var_1;
int *var_2;
var_1 = 130;
var_2 = &var_1;

assegna l'indirizzo di var_1 a var_2.
L'operatore * dice a var_2 di puntare a quell'indirizzo in altre parole di vedere il contenuto di quella locazione.


Facendo un esempio grafico :

+-----+ 1050 (indirizzo di var_1)
var_1 | 130 |
+-----+

var_2 = &var_1

+-----+ 1300 (indirizzo di var_2)
var_2 |1050 |
+-----+

quindi dicendo *var_2 si fara' si' che questa punti al contenuto dell'indirizzo 1050.
Nel linguaggio C esiste anche una stretta relazione tra array e puntatori.
Al contrario di un array o di una matrice che occupa in memoria lo spazio necessario per tutti gli elementi, un puntatore occupa solo lo spazio atto a contenere l'indirizzo.
Gli indirizzi occupano sempre lo stesso spazio indipendentemente dal tipo.
Ad esempio un array

int var[100];

occupa in memoria 100 volte lo spazio di un int (2 byte per 100)
mentre

int *var_1;

occupa solo lo spazio per un indirizzo ovvero pochi byte.
Eseguendo ora

var_1 = &var[0];

si ottiene che var_1 conterra' l'indirizzo del primo elemento del vettore var.
Incrementando var_1 faremo in modo che questo punti al successivo elemento.
var_1 + 1 e' l'indirizzo di var[1], var_1 + 2 quello di var[2] e cosi' via, mentre *(var_1 + 1) e' il contenuto di var[1].
Una cosa da tenere bene a mente e' il sistema che utilizza il linguaggio C nel momento in cui si somma un intero ad un puntatore.
Il valore prima di essere sommato viene moltiplicato per la lunghezza di memoria che occupa l'oggetto a cui punta.
Facciamo l'esempio di un puntatore ad interi ricordandosi che l'occupazione per ciascuno di questi e' di 2 bytes.
Supponendo che l'indirizzo del primo elemento del vettore sia 700
e che il puntatore punti appunto a questo, incrementando quest'ultimo per farlo riferire all'elemento successivo si otterra' che questo puntera' alla locazione 702.

Infatti sarebbe

700 + ( 1 * 2 )

D'altra parte dovrebbe risultare chiaro in quanto se il primo elemento e' posizionato in una certa locazione e questo occupa un certo numero di bytes, l'elemento successivo sara' spiazzato rispetto a quello precedente proprio della dimensione di quest'ultimo.
Il valore di un vettore senza indice corrisponde all'indirizzo iniziale del vettore stesso e quindi un assegnazione del genere

var_1 = &var[0];

corrisponde a

var_1 = &var;

L'utilizzo di puntatori porta alla scrittura di un codice piu' veloce anche se nessuno ci vieta di riferirci ad array e a matrici mediante l'utilizzo di indici.
Ad esempio

var[] equivale a *var
var[][] equivale a *var[] o a **var

Il discorso delle matrici e dei puntatori puo' essere esteso anche alle strutture ed alle funzioni.
Come avevamo gia' detto una struttura e' un insieme di variabili anche di tipo diverso raggruppate insieme.
Il metodo per utilizzare una struttura e' quello di introdurre la dichiarazione mediante la parola chiave struct

struct archivio {
char nome[30];
char via[20];
long stipendio;
};

Successivamente l'etichetta della struttura puo' essere utilizzata per definizioni di istanze della struttura stessa.
Infatti archivio e' a questo punto un nuovo tipo di dato che potra' servire per altre dichiarazioni come

struct archivio var_1;

La precedente dichiarazione specifica che var_1 e' una struttura di tipo archivio.
Sara' possibile ora accedere ai membri della struttura mediante l'utilizzo dell'operatore '.'.

Questo operatore ha la precedenza piu' alta tra tutti gli
operatori.

var_1.nome

L'assegnazione di un membro di una struttura avviene normalmente
Ad esempio :

var_1.stipendio = 780000;

E' anche possibile nidificare le strutture.

struct assunzioni {
struct archivio var_1;
char data_assunz[10];
int anni_lavoro;
};

struct assunzioni var_2;

Nel caso precedente si puo' accedere al membro 'nome' della struttura archivio con

var_2.var_1.nome


L'utilizzo dei puntatori puo' coinvolgere anche le strutture.

struct assunzioni *var_2;

specifica che var_2 punta ad una struttura di tipo assunzioni.
L'accesso ad uno dei membri della struttura avviene in questo caso mediante un nuovo operatore e precisamente

-> (segno meno seguito da maggiore)

Mantenendo valida la prima dichiarazione di struttura fatta negli esempi precedenti, l'accesso al membro 'nome' di questa verrebbe eseguito mediante

var_2->nome

L'equivalente con il vecchio operatore '.' sarebbe

(*var_2).nome

E' anche possibile eseguire delle dichiarazioni di matrici di strutture.

struct assunzioni var_2[20];

dichiara come una matrice di strutture var_2.

L'accesso ai membri avviene in questo caso specificando anche l'indice.

var[2].nome

Mentre molti compilatori non possiedono la possibilita' di passare come argomenti delle strutture, il compilatore Microsoft ha anche questa capacita'.
E' possibile fare questo passando soltanto l'indirizzo della struttura.

Esempio:

struct parametri {
int data_bit;
int stop_bit;
int bauds;
char parity[7];
};

main()
{
struct parametri var_1;
var_1.data_bit = 0x08;
var_1.stop_bit = 0x01;
......................
......................
setta(&var_1);
}

setta(par)
struct parametri par;
{
......................
......................
}

Ci si trova spesso davanti a circostanze in cui e' necessario un uso ricorsivo delle strutture stesse. Una dichiarazione del tipo

struct nodi {
char *stringa;
char *pointer;
struct nodi *precedente;
struct nodi *prossimo;
};

utilizza appunto in modo ricorsivo la struttura stessa. Non e' permesso che una struttura contenga un istanza di se stessa.
In questo caso pero' si parla di un puntatore a alla struttura e non di una struttura vera e propria.
Un utilizzo di una struttura ricorsiva potrebbe rivelarsi particolarmente utile nel caso di creazioni di alberi binari.
Un classico esempio di utilizzo e' portato da un word processor in cui l'organizzazione logica del testo potrebbe essere supportata su strutture del tipo appena visto.
Per ogni riga memorizzata dovrebbe apparire un puntatore al testo
della riga stessa, uno alla riga precedente e uno a quella successiva.
Un organizzazione di questo tipo eliminerebbe gran parte delle problematiche relative alle funzioni di cancellazione e inserimento.
Supponendo di voler inserire una nuova riga sarebbe sufficente
assegnare il nuovo testo e modificare i puntatori delle due righe tra le quali si vuole eseguire l'inserimento.
Con questo non voglio dire che una gestione in questo modo renderebbe semplicissima la cosa ma sicuramente la faciliterebbe notevolmente almeno per quanto riguarda l'organizzazione logica della progettazione.
Prima di concludere il discorso relativo alle strutture voglio riportare un metodo che permettera' di evitare di diver riscrivere tutta la trafila (xxxxx.yyyyy. ecc.) tutte le volte che si vuole accedere a un membro.
Dopo una dichiarazione

struct archivio {
char nome_cliente[15];
char cogn_cliente[15];
int codice;
} arc_clienti;

si potrebbe definire

#define cod (arc_clienti.codice) ecc. ecc.

e quindi accedere al membro codice, ad esempio, mediante

cod = 123; invece di arc_clienti.codice = 123;


Unioni

Parlando dei puntatori ho preso la palla a balzo per includere nel paragrafo anche le strutture anche se si sarebbe potuto dedicargli uno spazio loro.
In ogni caso i concetti fondamentali sono stati trattati anche se poi, come per tutto il resto, la pratica e gli errori che scaturiranno da questa saranno i migliori maestri.
Dedico ancora un po' di spazio ad un tipo molto simile alle strutture e cioe' alle unioni.
La differenza tra un unione e una struttura e' che la seconda alloca una memoria sufficente a contenere tutti campi indicati in questa mentre la prima lo fa' per contenere un solo campo alla volta, in genere quanta ne richiede il campo maggiore.
Le unioni non possono contenere come campi i bitfields che invece sono accettati all'interno di una struttura.
Il metodo di accesso ai membri di un unione e' uguale a quello utilizzato per le strutture ovvero mediante gli operatori '.' e

-->'.

Un esempio

struct WREGS {
unsigned ax;
unsigned bx;
unsigned cx;
};

struct BREGS {
unsigned char al;
unsigned char ah;
unsigned char bl;
unsigned char bh;
unsigned char cl;
unsigned char ch;
};

union regs {
struct WREGS x;
struct BREGS h;
};

Dopo la dichiarazione

union regs inregs;

potremo accedere ai membri mediante

inregs.x.cx

ad esempio

inregs.h.bh = 0x02;



Funzioni che ritornano array e puntatori a funzioni

La stessa tecnica usata per passare una struttura da una funzione ad un altra mediante un puntatore e' applicabile anche alle funzioni stesse.
Queste ultime infatti possono ritornare valori di ogni tipo eccetto arrays anche se possono in ogni caso ritornare un puntatore ad un vettore o ad una funzione.
In questo caso la funzione deve essere dichiarata con il tipo che ritornera'.
Non bisogna confondere la dichiarazione del tipo ritornato con la dichiarazione di un puntatore a funzione.
Ogni funzione, come nel caso delle variabili, possiedono un indirizzo ed e' quindi permesso usare dei puntatori a queste anche se in questo caso non sara' possibile alterare l'oggetto puntato.

Una dischiarazione del tipo

char (*funz)()

e' diversa da

char *funz()

che e' quella che interessa a noi per la restituzione di un puntatore a char.
Infatti nel primo caso la dichiarazione afferma che funz e' un puntatore ad una funzione che ritorna un char mentre nel secondo caso funz e' una funzione che ritorna un puntatore ad un char.
Un esempio di dichiarazione di puntatore a funzione che ritorna un puntatore a char e' il seguente

char *((*funz)())

Molte volte una funzione potrebbe servire ad elaborare dati di tipi diversi.
Utilizzando un puntatore ad una funzione facciamo in modo che il programma chiamante non si preoccupi di nulla se non dell'indirizzo della funzione stessa.

Fine Parte 2

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