I CARATTERI

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.