I CARATTERI • Ogni carattere viene memorizzato come un numero intero • “smallest addressable unit of the machine that can contain basic character set. It is an integer type. Actual type can be either signed or unsigned depending on the implementation.” • • • • Dichiarazione di un carattere: char c = ‘p’; La conversione classica carattere ßà numero è codificata all’interno di una tabella standard chiamata ASCII table • • Il segno (se non si definisce signed o unsigned) è definito dall’implementazione (-funsigned-char su GCC per forzarlo) Serve a farci della “matematica” rappresentazione di 1 carattere con 7 bit Domanda: • • char c = ‘1’; printf(“c=%d\n”, (int)c ); //cosa stampa? ASCII TABLE UNICODE • • Problema: come rappresentare le lettere non comprese dal’alfabeto americano • ad. es. ò, è, ù • ma anche le lettere cirilliche/ideogrammi cinesi etc. Unicode: sistema di codifica di più di 110000 caratteri, gestione e rappresentazione • • • ad es. gli arabi scrivono da destra a sinistra standard (attualmente v 6.3) mantenuto dall’Unicode Consortium Codifica: tra le piu’ usate, UTF-8 • • Se il carattere è nella tabella ASCII, allora usiamo la codifica ASCII • Altrimenti scriviamo il valore codificato (fino 4 byte) Unicode != UTF • • Unicode: character set (mappa ad ogni carattere un valore univoco), UTF: codifica (come memorizzare/ inviare quel valore) “UTF-8 and Unicode cannot be compared. UTF-8 is an encoding used to translate binary data into numbers. Unicode is a character set used to translate numbers into characters.” UNICODE: ESEMPIO LE STRINGHE • Array di char terminate dal carattere NULL ‘c’ • NULL è il carattere ‘\0’ ovvero il numero 0 • “null terminated array of char” ‘i’ ‘a’ ‘o’ • Esempio: • char mia_stringa[] = “ciao”; • Se una stringa viene inizializzata in questo modo, la terminazione è automatica. La dimensione è quindi 5 byte • Metodo alternativo: • char mia_stringa[5] = {‘c’, ‘i’, ‘a’, ‘o’, ‘\0’}; ‘\0’ ESERCIZIO CON STRINGHE • • Accettare una stringa, memorizzarla e stamparla a video come stringa e come insieme di numeri • ovvero l’ASCII code corrispondente ai suoi caratteri Funzioni utili: • • • • • scanf(“%s”, mia_stringa); // per accettare input e scriverli dentro l’array printf(“%s”, mia_stringa); //per stampare la stringa printf(“%c”, mia_stringa[0]); // per stampare un carattere scanf(“%c”, &mia_stringa[0]); // per accettare un carattere scanf("%[^\n]s", mia_stringa); //accetta fino al newline • Cosa succede se scriviamo più caratteri della dimensione dell’array? • Cosa succede se non terminiamo con NULL la stringa? SICUREZZA E BUFFER OVERFLOW? http://www.dmi.unipg.it/~bista/didattica/sicurezza-pg/bufferoverrun/letture-buffer-overrun/Buffer-Overflows-0.7.pdf I PUNTATORI • Variabili i cui valori sono indirizzi di memoria • Variabili che contengono l’indirizzo di un’altra variabile • I puntatori hanno un tipo (ad es. puntatore ad intero) • Esempio: int *mio_puntatore • int *a, b; //dichiara un puntatore di tipo a e un intero di tipo b • Buona pratica: Inizializzare i puntatori a NULL • esempio: int *mio_puntatore = NULL; • costante simbolica definita come 0 OPERATORE DI INDIRIZZO • L’operatore “&” restituisce l’indirizzo di una variabile OPERATORE DI INDIREZIONE int a = 5; int *myptr = NULL; myptr = &a; int a = 5; int *myptr = NULL; myptr = &a; • L’operatore “*” il valore della variabile puntata da un puntatore • “dereferenziare” un puntatore printf(“a=%d”, *myptr); printf(“myptr=%p”, myptr) • Gli operatori possono essere concatenati (ad es **a) • Se applicati insieme (ad es *&a) restituiscono la variabile originaria (ad es a) PASSAGGIO DI ARGOMENTI PER RIFERIMENTO • In C tutti gli argomenti vengono passati per valore • Passando il puntatore ad una variabile, si “emula” il passaggio per riferimento void make_square(int *); int main () { int test = 200; • • Molto utile per motivi di efficienza • Utile per far modificare ad una funzione più variabili E’ un passaggio delicato… #include <stdio.h> • • Quando si può, passare per valore per il principio del privilegio minimo (facilita il debug) oppure usare const (vedi dopo) make_square(&test); printf(“test=%d\n”, test); return 0; } void make_square(int *n){ *n = *n * *n; } QUALIFICATORE CONST • Se una variabile non cambia all’interno di una funzione, deve essere dichiarata come const • esempio: const int a = 10; • Valido anche per gli argomenti: esempio prova_funzione (const int b) • Non viene modificata la copia di b • Qualora venga modificata, errore in compilazione Attenzione! void test(char* c); puntatore non costante a dati non costanti void test(const char* c); puntatore non costante a dati costanti void test(char * const c); puntatore costante a dati non costanti void test(const char * const c); puntatore costante a dati costanti NOTA: “const char *c” e “char* const c” sono la stessa cosa PUNTATORI E ARRAY Esempio: int my_array[10]; • my_array (senza indice) è un puntatore costante al primo elemento dell’array • il passaggio di un array ad una funzione avviene quindi per riferimento • E’ possibile far puntare un puntatore al primo elemento dell’arrary • int *ptr; • ptr = my_array; // equivalente a ptr = &my_array[0] • … muoverlo per far puntare diversi elementi dell’array • *(ptr + 3); // notazione puntatore/offset • ptr[3]; //notazione puntatore/indice • sono equivalenti ARRAY DI PUNTATORI Esempio: array di stringhe const char *mie_stringhe[3] = {‘pippo’, ‘pluto’, ‘ciao’}; mie_stringhe[0] mie_stringhe[1] mie_stringhe[2] p i p p o \0 p l u t o \0 c i a o \0 Che differenza c’e’ con un array bidimensionale di caratteri? ARGC - ARGV • La funzione main accetta tre parametri • int argc: numero di argomenti passati alla funzione (+1) • ad es. ./main test1 test2 à argc = 3 • char *argv[]: array di stringhe (o array di puntatori a carattere) che rappresentano i valori degli argomenti • • • ad es. ./main test1 test2 à argv[1] = ‘test1’ Il primo argomento è sempre il nome del programma stesso argv[argc] è NULL • char *envp[]: array di stringhe che rappresentano i valori delle variabili di ambiente • poco usata (ricerca lineare) meglio “getenv” (lib standard) ESEMPIO OPERATORI *E& • char c, *p, **pp; • c = ‘x’; • p = &c; //ok • pp = &p; //ok • printf(“%c\n”, **pp); //ok scrive c • pp = &&c; //errore! PUNTATORI A FUNZIONE Per dichiarare un puntatore a funzione: • tipo_restituito (*nome_puntatore_a_funz)(arg1, arg2 …); • Esempi: • int (* pippo)(char c, int *p); • Dichiaro un puntatore a funzione che si chiama pippo e puo’ puntare ad una funzione che accetti come parametri un char e un puntatore a intero, e che ritorni un intero. • int *pippo(char , int *p); • Questo non è un puntatore a funzione, ma un prototipo di funzione Esempio di utilizzo: • • double somma( double a, double b) ; /* dichiarazione */ int main() { • • • • • • } …. double (*ptrf) ( double g, double f); double c ptrf = somma; c = ptrf (A,B); CASTING DI PUNTATORI E’ possibile fare il casting dei puntatori cosi’ come per gli altri tipi • char *c = ‘ciao’; • short x = (short*)c; • A volte è usato “void *” ad indicare un puntatore che prima di essere dereferenziato obbliga al casting NON E’ COSI’ SEMPLICE… char *c = “ciao”; “string literals”, è sempre di tipo const char[N] c != c2 char *c2 = “ciao”; char *c = “ciao”; char *c2 = “pippo”; c = c2 Non stiamo copiando le stringhe ma stiamo facendo puntare due puntatori alla stessa stringa • In C, manipolare le stringhe è come manipolare degli array di numeri • E’ per questo che non viene usato in alcuni contesti (ad es il web) • Stesso discorso per la concatenazione di stringhe… STRINGHE: COPIARE E CONFRONTARE <string.h> Copiare char *strcpy(char *s1, const char *s2); Copia s2 in s1, ritorna s1 char *strncpy(char *s1, const char *s2, size_t n); Copia al massimo n caratteri da s2 in s1, ritorna s1 char *strcat(char *s1, const char *s2); Concatena s2 in fondo a s1 char *strncat(char *s1, const char *s2, size_t n); Concatena al massimo n caratteri di s2 in fondo a s1 Confrontare int strcmp(char *s1, const char *s2); Confronta s1 e s2, ritorna 0 se sono uguali int strncpy(char *s1, const char *s2, size_t n); Confronta n caratteri, ritorna 0 se sono uguali STRINGHE: CONVERSIONE <stdlib.h> double strtod(const char *nPtr, char **endPtr); converte la stringa “nPtr” in un double, restituendolo. endPtr è l’indirizzo di un puntatore a carattere che viene fatto puntare subito dopo la fine della stringa convertita long strtol(const char *nPtr, char **endPtr, int base); converte la stringa in long unsigned long strtoul(const char *nPtr, char **endPtr, int base); converte la stringa in unsigned long STRINGHE: FORMATTAZIONE Scrivere una stringa formattata • sprinf(mia_stringa, “pippo=%d, pluto=%d”, pippo, pluto); • come printf, ma scrive su stringa e non su std output Leggere una stringa formatta • sscanf(mia_stringa, “pippo=%d, pluto=%d”, &pippo, &pluto) • presa la stringa precedente, salviamoci le variabili STRINGHE: ALTRE FUNZIONI UTILI char *strchr(const char *s, int c); ritorna la prima occorrenza di c in s, oppure NULL char *strstr(const char *s1, const char *s2); ritorna il puntatore alla prima occorrenza della stringa s2 in s1 o NULL char *strtok(char *s1, const char *s2); Separa la stringa s1 in base ai caratteri definiti in s2. La prima chiamata restituisce l’indirizzo del primo token. Le chiamate successive, se s1==NULL, restituisono i token successivi, oppure NULL se non ci sono altri token. PER MAGGIORI INFO apt-get install manpages-dev apt-get install manpages-posix-dev (e ovviamente se non l’avete ancora fatto installate “man”) e quindi: man strlen ESERCIZIO: PRODOTTO SCALARE Realizzare un programma che accetti come parametri due vettori e ne ritorni il prodotto scalare Specifiche tecniche: • I due vettori verranno passati come stringhe al programma e l’output sarà un numero che verrà sia stampato a video che impostato come valore di ritorno (return X) • Ogni elemento del vettore è un intero con segno (puo’ essere positivo o negativo) • usare i puntatori • scrivere una funzione “parser” (input stringa à output vettore) • ESEMPIO • • • ./mio_programma “{1,2,3,4}” “{0,1,2,0}” à calcolo 1*0 + 2*1 + 3*2 + 4*0 = 8 return 8 ESERCIZIO IN AULA Scrivere una funzione che cambi il valore di un puntatore inizializzandolo a NULL. OPERAZIONI CON LA MEMORIA char *memcpy(void *s1, const void *s2, size_t n); Copia n caratteri da s2 in s1, ritorna s1 char *memmove(void *s1, const void *s2, Copia n caratteri da s2 in s1 utilizzando size_t n); un array temporaneo, ritorna s1. Ad esempio per spostare di un byte un insieme di caratteri int memcmp (void *s1, const void *s2, size_t n); Ritorna 0 se i primi n caratteri sono uguali char *memchr(const void *s1, int c, size_t n); Cerca la prima occorrenza di c in s1. Ne ritorna il puntatore o NULL. void *memset(void *s, int c, size_t n); Copia c nei primi n caratteri dell’oggetto puntato da s.
© Copyright 2024 Paperzz