Post on 23-Feb-2019
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:
• 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’};
‘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?
ESERCIZIO IN AULA Calcolare se una frase (input) è palindroma: Radar
Otto
ecc.
SICUREZZA E BUFFER OVERFLOW? http://www.dmi.unipg.it/~bista/didattica/sicurezza-pg/buffer-overrun/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
int a = 5;
int *myptr = NULL;
myptr = &a;
OPERATORE DI INDIREZIONE
• L’operatore “&” restituisce l’indirizzo di una variabile
int a = 5;
int *myptr = NULL;
myptr = &a;
printf(“a=%d”, *myptr); printf(“myptr=%p”, myptr)
• L’operatore “*” il valore della variabile puntata da un puntatore • “dereferenziare” un puntatore
• Gli operatori possono essere concatenati (ad es **a) • Se applicati insieme (ad es *&a) restituiscono la variabile
originaria (ad es a)
CREAZIONE DI UNA STRINGA 1. char s[10] = “prova”;
Alloca un array di 10 caratteri e lo inizializza a “prova\0” 2. char s[] = “prova”;
Alloca un array di 5 caratteri e lo inizializza a “prova\0” 3. char *s = “prova”;
Crea una stringa “prova\0” e fa puntare da s (non è detto che sia scrivibile)
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!
ESERCIZIO CON ARRAY Giochiamo a filetto! • Creare un array multidimensionale 3 X 3 • Finchè la partita non è conclusa:
• Far inserire la scelta dell’utente (numero da 1 a 9) • Far generare al computer una scelta random • Calcolare se la partita è conclusa
• Stampare il risultato (hai vinto/perso/pari)
PUNTATORI E ARRAY • 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
Esempio: int my_array[10];
PASSAGGIO DI ARGOMENTI PER RIFERIMENTO
#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); }
• 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)
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
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
Attenzione!
PUNTATORI AD ARRAY int a[8]; int *pa = a; // metodi uguali
a[2] = 4;
*(pa+2) = 4;
Possiamo usare i puntatori per accedere agli elementi dell’array!
Aritmetica dei puntatori incrementiamo il puntatore di sizeof( ELEMENTO_PUNTATO)
PUNTATORI AD ARRAY MULTIDIMENSIONALI int a[4][2] = {{1,2,3,4},{5,6,7,8}};
ARRAY DI PUNTATORI Esempio: array di stringhe const char *mie_stringhe[3] = {“pippo”, “pluto”, “ciao”};
c i a o \0
p i p p o \0
p l u t o \0
mie_stringhe[0] mie_stringhe[1] mie_stringhe[2]
Che differenza c’e’ con un array bidimensionale di caratteri?
ESERCIZIO
Creare una funzione void che • accetti un puntatore a una stringa e
che lo modifichi per puntare al primo carattere ‘s’ o a NULL se non lo trova
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)
int main(int argc, char *argv[], char *envp[])
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);
• } • Proviamo ad inserire anche la funzione “sottrazione”…
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”;
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…
è sempre di tipo const char[N]
STRINGHE: COPIARE E CONFRONTARE 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
<string.h>
Confrontare
int strcmp(char *s1, const char *s2); Confronta s1 e s2, ritorna 0 se sono uguali
int strlen(char *s1); Ritorna la lunghezza della stringa
STRINGHE: FORMATTAZIONE Scrivere una stringa formattata
• sprintf(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
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
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: 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.
ESERCIZIO: PRODOTTO SCALARE Realizzare un programma che accetti come parametri due vettori e ne ritorni (return del main) 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 double con segno (puo’ essere positivo o negativo) - max
100 elementi in ogni vettore (100 numeri) • i vettori hanno stesso numero di argomenti e le virgole parantesi sono corrette; gli spazi
possono essere ovunque e in numero arbitrario tra i numeri, le virgole e le parenetesi • il valore di ritorno deve essere arrotondato all’intero piu’ vicino • Le virgolette non compaiono nell’argomento (non sono in argv): servono per passare
tutto il vettore come unico argomento, altrimento ad ogni spazio ci sarebbe un argomento nuovo
• Piu’ file .c e almeno un .h, NO GOTO
• ESEMPIO • ./mio_programma “{1,2,3, 4}” “{0,1, 2,0}” • à calcolo 1*0 + 2*1 + 3*2 + 4*0 = 8 • return 8
Attenzione agli spazi!
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, size_t n);
Copia n caratteri da s2 in s1 utilizzando 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.