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. Il segno (se non si definisce signed o unsigned) è definito dall implementazione (-funsigned-char su GCC per forzarlo) Serve a farci della matematica Dichiarazione di un carattere: char c = p ; La conversione classica carattere ß à numero è codificata all interno di una tabella standard chiamata ASCII table 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 NULL è il carattere \0 ovvero il numero 0 null terminated array of char Esempio: c i a o \0 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 };
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 int a = 5; int *myptr = NULL; myptr = &a; OPERATORE DI INDIREZIONE L operatore * il valore della variabile puntata da un puntatore dereferenziare un puntatore int a = 5; int *myptr = NULL; myptr = &a; 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 Molto utile per motivi di efficienza Utile per far modificare ad una funzione più variabili E un passaggio delicato Quando si può, passare per valore per il principio del privilegio minimo (facilita il debug) oppure usare const (vedi dopo) #include <stdio.h> void make_square(int *); int main () { } int test = 200; 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); void test(const char* c); void test(char * const c); void test(const char * const c); puntatore non costante a dati non costanti puntatore non costante a dati costanti puntatore costante a dati non costanti 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 ; char *c2 = ciao ; string literals, è sempre di tipo const char[n] c!= c2 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); char *strncpy(char *s1, const char *s2, size_t n); char *strcat(char *s1, const char *s2); char *strncat(char *s1, const char *s2, size_t n); Copia s2 in s1, ritorna s1 Copia al massimo n caratteri da s2 in s1, ritorna s1 Concatena s2 in fondo a s1 Concatena al massimo n caratteri di s2 in fondo a s1 Confrontare int strcmp(char *s1, const char *s2); int strncpy(char *s1, const char *s2, size_t n); Confronta s1 e s2, ritorna 0 se sono uguali Confronta n caratteri, ritorna 0 se sono uguali
STRINGHE: CONVERSIONE <stdlib.h> double strtod(const char *nptr, char **endptr); long strtol(const char *nptr, char **endptr, int base); unsigned long strtoul(const char *nptr, char **endptr, int base); 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 converte la stringa in long 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); char *strtok(char *s1, const char *s2); ritorna il puntatore alla prima occorrenza della stringa s2 in s1 o NULL 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); char *memmove(void *s1, const void *s2, size_t n); int memcmp (void *s1, const void *s2, size_t n); char *memchr(const void *s1, int c, size_t n); void *memset(void *s, int c, size_t n); Copia n caratteri da s2 in s1, ritorna s1 Copia n caratteri da s2 in s1 utilizzando un array temporaneo, ritorna s1. Ad esempio per spostare di un byte un insieme di caratteri Ritorna 0 se i primi n caratteri sono uguali Cerca la prima occorrenza di c in s1. Ne ritorna il puntatore o NULL. Copia c nei primi n caratteri dell oggetto puntato da s.