Le struct

Realizzare un programma che acquisisca da tastiera un numero intero senza segno n. Se l'utente inserisce un numero maggiore di 10 si assuma n = 10. Successivamente il programma deve acquisire una lista di coordinate di n punti nel piano; le coordinate di ogni punto, che sono dei numeri in virgola mobile, devono essere inserite sulla stessa riga per es.:

1.23 4.5678

Suggerimenti generali

Conviene dichiarare una struttura dati, denominata per esempio struct punto_t, che contenga i due campi x e y di tipo double (in S7.2 c'è un esempio di dichiarazione con campi di tipo int). È possibile utilizzare anche la typedef spiegata in S7.3, ma in questo tutorial non verrà utilizzata. Dichiara nel main un vettore di MAXN strutture di tipo struct punto_t, per es.

struct punto_t vett[MAXN];

MAXN sia una macro dichiarata con #define e posta uguale al massimo numero di punti da memorizzare (10). Dopodiché va letto il valore di di n.

Per la lettura dei singoli punti può essere creata una funzione come la seguente:

struct punto_t leggi_punto(void);

La funzione utilizzerà la fgets per leggere dal file una riga di testo alla volta, e per ciascuna riga letta verrà chiamata la sscanf per estrarre dalla riga i due numeri corrispondenti alle coordinate del punto (vedi S7.2.4 per un esempio di uso della sscanf in abbinamento alle struct). Dichiarerà una variabile di tipo struct punto_t i cui campi saranno assegnati con la sscanf; tale variabile verrà restituita con la return.

1) Stampa dei punti

Stampare a video (uno per riga) i punti acquisiti, utilizzando il seguente formato:

[PUNTI]
(1.000, 2.000)
(3.000, 4.000)
(5.000, 6.000)

Suggerimenti

Visto che nel corso del programma servirà stampare varie volte i punti, conviene definire una funzione come

void stampa_punto(struct punto_t p);

La funzione stamperà le coordinate di un singolo punto p passato come parametro usando il formato richiesto, ovvero:

  • due numeri double, tra parentesi tonde e separati da virgola
    • lo spazio dopo la virgola è importante, non dimenticarlo!
  • ciascun numero deve avere 3 cifre dopo la virgola
  • ricorda che per stampare un singolo numero con il formato richiesto si può usare lo specificatore %.3lf.

La funzione stampa_punto verrà chiamata in un ciclo for all'interno del main per stampare tutti i punti memorizzati nel vettore vett dichiarato precedentemente.

2) Distanza dall'origine

Calcolare la distanza dall'origine (il punto (0, 0)) di ogni punto acquisito. Stampare le distanze in ordine corrispondente a quello dei rispettivi punti. Utilizzare il seguente formato:

[DISTANZE]
2.236
5.000
7.810

Suggerimenti

Crea innanzitutto una funzione chiamata distanza, come la seguente:

double distanza(struct punto_t p1, struct punto_t p2);

La funzinoe calcola la distanza tra due punti generici p1 e p2 (una funzione simile è mostrata in S7.2).

La distanza d tra i punti p_1 = (x_1,y_1) e p_2 = (x_2, y_2) è data dalla formula (attenzione che non è codice C):

d = sqrt((x_2-x_1)^2 + (y_2-y_1)^2)

dove sqrt è la funzione che calcola la radice quadrata, mentre ^ indica un elevamento a potenza.

Per poter usare la funzione per il calcolo della radice quadrata in C, bisogna:

  • includere math.h (con la direttiva #include)
  • compilare il programma aggiungendo l'opzione -lm (elle minuscola + emme minuscola) alla linea di comando (su alcuni sistemi non è necessario), ovvero, supponendo che il programma si chiami struct.c, l'istruzione per la compilazione è:
cc -Wall struct.c -lm

Per risolvere il problema, la funzione distanza verrà chiamata per ciasun elemento del vettore vett con un ciclo for nel main. L'istruzione che calcola la distanza d da stampare sarà simile alla seguente:

d = distanza(origine, vett[i]);

dove origine è una struttura che contiene le coordinate dell'origine e può essere dichiarata come segue:

struct punto_t origine = {0, 0};

3) Punti interni

Si richieda in input i dati di un rettangolo avente i lati paralleli agli assi cartesiani. Dire quali sono i punti contenuti nel rettangolo. Il rettangolo parallelo agli assi sia definito da una coppia di punti A e B.

rettangolo

Per acquisire il rettangolo si acquisiscano quindi i 2 punti A e B come ad esempio:

0.0 5.0
5.0 -1.0

Il programma dovrà cercare quali punti, tra quelli acquisiti in precedenza, siano interni al rettangolo. Si considerino contenuti anche i punti che si trovino sul bordo del rettangolo. Si stampino i punti interni usando il formato:

[INTERNI]
(1.000, 2.000)
(3.000, 4.000)

Suggerimenti

  • si leggano i due punti A e B utilizzando la funzione leggi_punto già implementata
  • può essere utile creare una struct rettangolo_t i cui due campi siano di tipo struct punto_t (un esempio simile è riportato nella Sezione di approfondimento 7, nel capitolo delle strutture)
  • si può allora definire una funzione come
int interno(struct punto_t p, struct rettangolo_t r);

che, dato un punto p e un rettangolo r restituisce 1 se il punto è interno al rettangolo, e 0 altrimenti.

Questa funzione può essere chiamata in un ciclo for nel main passando di volta in volta tutti i punti nel vettore di punti, e stampando il punto con la funzione stampa_punto se le coordinate sono interne.

4) Area del rettangolo

Calcolare l'area del rettangolo acquisito al punto precedente. Stampare il valore dell'area col seguente formato:

[AREA]
30.000

Suggerimenti

Creare una funzione tipo:

double area_rettangolo(struct rettangolo_t r);

la quale, dato un rettangolo r, ne restituisce l'area.

NOTA: la lunghezza di base e altezza del rettangolo, essendo i suoi lati paralleli agli assi, si può calcolare ripettivamente come la differenza delle coordinate x e y dei punti estremi (in valore assoluto).

5) Coppia dei punti più lontani

Trovi la coppia di punti acquisiti più lontani tra loro. Si stampino i due punti con il seguente formato:

[COPPIA]
(5.000, 6.000)
(1.000, 2.000)

Suggerimenti

  • bisogna confrontare ciascun punto nel vettore con tutti gli altri
    • per questo sono sufficienti due cicli for annidati: quello più esterno con i che va da 0 a n-1, mentre quello più interno con j che va da i+1 a n
    • una variabile max memorizzerà il valore massimo corrente
  • la distanza tra due punti può essere calcolata con la funzione distanza implementata per risolvere il punto precedente corrispondente
  • quando la distanza tra i due punti correnti supera max, gli indici dei punti nel vettore che danno luogo alla distanza corrente sono memorizzati in due variabili max_i e max_j

Questo procedimento può essere implementato in una funzione come la seguente:

void indici_max_dist(struct punto_t vett[], int len, int *max_i, int *max_j);

Essa restituisce gli indici max_i e max_j nel vettore vett di lunghezza len che corrispondono ai punti che si trovano alla maggior distanza tra loro.

Verifica automatica

Si utilizzi il tool pvcheck di verifica automatica per testare il corretto funzionamento del programma (maggiori informazioni circa l'uso di pvcheck sono disponibili qui).

Il file contenente i test è struct.test.

Il comando da eseguire per il test è il seguente:

./pvcheck -f struct.test ./a.out

Nella prossima pagina potrai esaminare un esempio di soluzione dell'esercizio.