C
Gerarchia Elaboratore
- Digital Logic
- Microarchitecture
- Instruction Set Architecture
- Operating System
- Assembly
- High Level Language
Processo da file sorgente a eseguibile: vedi Compilazione e Linking
- Pre-processing ~ .c
- Compilazione ~ .s
da qui il linguaggio diventa
architecture specific
- Assembler ~ .o
- Linking ~ .o
Librerie: .so shared object
Gnu Compiler Collection - gcc
- Front-end per il processo di compilazione-assemblaggio-linking
- -c ferma dopo l’assemblaggio
- -S ferma dopo la compilazione
- -E ferma dopo il pre-processore
- stampa in standard output
Il C nasce come linguaggio per lo sviluppo del OS Unix
Il C e' un linguaggio tipato, ogni valore deve essere di un determinato tipo
Un file sorgente C puo' essere composto da:
- direttive al preprocessore
- dichiarazione tipi
- dichiarazioni variabili globali
- definizioni di funzioni come metodi Java ma non appartengono ad alcuna classe
- funzione main()
direttive al preprocessore
Le < > indicano al preprocessore che il file va cercato in percorsi predefiniti
include
es
#include <stdio.h>
headers .h
contengono le dichiarazioni delle funzioni fornite dalle librerie
define
definisce nomi simbolici per valori costanti
Associa due valori testuali
- il numero di caratteri del nome simbolico é limitato: era 8
- il pre-processore legge i moduli C in un passo
macro
una funzione ha un overhead – le macro no
Associa ad una signature una espressione
ifndef
se la costante simbolica non specificata non é stata ancora definita allora procede fino al endif
- per evitare
inclusioni multiple
ifdef
else
endif
Variabili
caratterizzate da un quadrupla (nome, tipo, indirizzo, valore)
L’indirizzo identifica la locazione di memoria alla quale il valore della variabile e' memorizzato e' assegnato dal OS a runtime o a compilazione
#include <stdio.h>
int i = 10;
int *ptr = &i;
int main(){
printf("Indirizzo di i: \t%p\n", ptr);
printf("Indirizzo di prt: \t%p\n", &prt); //il tipo di questa espressione e':
//puntatore di puntatore a int
}
variabili globali
definite in un modulo possono essere usate da altri moduli previa dichiarazione
- é buona regola definire le variabili globali in un intestazione
:ID: a862cf55-89ce-4612-88e9-ee94fd4b7b50
- definire una variabile globale
static
significa renderla visibile solo nel modulo di definizione
int error_code;
extern int error_code;
Sequenze di escape
\ sequenza
\n newline
\t tabulazione
Specificatori Formato
% specificatore
%d variabile
%p pointer
entry point
unico: int main()
tipi primitivi
Nella definizione originaria di D.Ritchie il linguaggio non stabilisce alcuna dimensione per i dati in base al loro tipo
- per portabilita' non si deve produrre un codice C dipendente dalla dimensione dei dati
int i;
short j = ;
int *ptr = NULL; //tipo: puntatore a interi
Non esistendo tipo Boolean:
- 0 == false
- valore diverso da 0 == true
int
short
long
unsigned int
float
double
char
void
es
funzione che non restituisce valore
*
tipo puntatore
SEGV
Segmentation Fault
- Puntatore non inizializzato non e' definito correttamente e punta a un’area di memoria protetta
enum
tipo enumerativo Permette di specificare valori opzionali
- enum nome { valore=
indicedipartenza
[, valore]* }
Assegna un indice ad ogni valore, si puó anche specificare l’indice di partenza Possibile definire un enumerazione per false, true
* a funzione
La sintassi é tipo ritorno (* nomefunzione)([tipo argomenti])
puntatore in memoria instruzioni: la prima istruzione della funzione in particolare
- il punto é che si crea una variabile, quindi durante l’esecuzione potrá cambiare.
- uno stesso codice che utilizza un puntatore a funzione chiamerá funzioni diverse in momenti ed esecuzioni diverse
- punterá sempre a funzioni con un simile prototipo
Funzioni
printf(char *format,…)
NB per fine linea
- Windows: \r\n
- Unix: \n
- Macintosh: \r
scanf(char *format,…)
passo riferimenti a variabili dove memorizzare cio' che e' letto dallo stream di input, secondo il formato specificato dal programmatore
-
se lo stream e' vuoto la scaf si mette in attesa, l’OS lo inserisce in uno stato di wait fino a che lo stream non sara' riempito
-
Restituisce il numero di conversioni avvenute con successo
Utilizzando la scanf
con il formato %s si leggono dallo stdin parole, ignorando spazi bianchi.
NB
va passato come secondo argomento l’indirizzo di una variabile puntatore a char che contenga un numero di caratteri sufficientemente grande.
getchar()
restituisce un int(4 byte) per trattare altri casi oltre ai caratteri(1 byte)
Strutture Dati
array []
-
un array in C é sempre un
const
, un puntatore al primo elemento -
le stringhe in realtá sono codificate con un array di
char
-
per modificare i valori sono usati i puntatori
-
es array di char di stringhe
char *weekdays[] = {"Mon", "Tue", "Wen", "Tue", "Fri"}; // un puntatore ad un puntatore // al primo oggetto dell'array
-
NB sull’uso di
sizeof()
sizeof(a)/sizeof(int) per sapere quanti interi conta l’array a (sizeof restituisce dimensioni in byte) MA ció non funziona se l’array e' passato come argomento di una funzione, in quanto sizeof(a) restituirá la dimensione del puntatore- il problema si puó risolvere passando un altro argomento n numero di argomenti
Per matrici
la memoria é allocata linermente, indirizzo:
a+i*LRighe+j
Perció il compilatore ha bisogno di sapere la dimensione della seconda grandezza dell’array a[r][c]
void f(int a[][4]);
// dimensioni diverse per ogni riga:
int r1[1] = {1};
int r2[2] = {2, 2};
int r3[3] = {3, 3, 3};
int *a[] = {r1, r2, r3};
struct
Raggruppa dati di natura diversa
- etichetta
- non obbligatoria
- campi
- a cui si accede con
a.x
- a cui si accede con
A differenza dagli array posso assegnare tra loro le strutture definite utilizzando la stessa etichetta Il nome della struttura é interpretato come il valore della struttura in memoria. Questo tranne che nel caso sia lvalue in caso di assegnamento Infatti gli struct sono passati per valore, sempre a differenza degli array che vengono passati per riferimento
- ogni volta che una struct sono passate o restituite vengono fatte copie dei dati poste sullo stack che vengono poi perse alla fine delle funzioni
- spesso allora si passano per riferimento con
void funzione(struct point *a)
- cambia la sintassi utilizzando
a->x
per indicare il campo da modificare- concide con il deferenziamento:
(*a).x
- concide con il deferenziamento:
- cambia la sintassi utilizzando
- spesso allora si passano per riferimento con
sizeof()
- accetta anche le struct
- spesso non coincide con le dimensioni dei suoi campi
- questo per i vincoli di allineamento
typedef
-
typedef
tipobase
tipodefinito
Su questi tipi non viene attuato un controllo stretto da parte del compilatore
typedef enum { FALSE, TRUE } boolean;
union
Zona di memoria condivisa di diverse variabili, di tipi diversi. La sintassi é simile a una struct.
- appunto memorizza in unione
Ridirigere lo standard input
programma < fileInput.txt Per utilizzare un file come stream di dati per
- scanf
- getchar
Traps al Sistema Operativo
Il Sistema Operativo gestisce una tabella di tutti i file aperti
- ogni record memorizza le informazioni di quella particolare istanza
- modalitá
- offset
- …
Funzioni di piú alto livello che interfacciano sui descrittori dei file
-
open()
-
read()
- viene passato:
- un fd
- un void * ad un buffer di dimensione necessaria (per poter contenere count byte)
- una size_t count
- viene passato:
-
write()
-
lseek()
-
close()
Funzioni che utilizzano gli stream
- fopen()
- restituisce un FILE *
- prende come parametri directory e flag r,w
- fread()
- fgets()
- fseek()
- fclose()
Allocazione dinamica della memoria dati
- malloc(size_t size)
- non inizializza a 0
- calloc(size_t nmemb, size_t size)
- inizializza a 0
- alloca pe n-membri di size dimensione
- realloc()
- free()
Queste funzioni lavorano su void * ad aree dello heap