[C] Dubbi cast e define

studente_studente
Buonasera, ho qualche "problema" con l'uso del costrutto cast (1) e la direttiva define (2).

(1) Quando converto una stringa con atof, essa mi ritorna un valore di tipo double. Sono nel caso in cui questa conversione è fatta all'interno di una funzione che però mi ritorna un float!! In generale, visto che ormai ho il dubbio, questo mi piacerebbe saperlo anche per altri casi: posso forzare le variabili double ad essere di tipo float o rischio di perdere informazioni? Nei libri e sul web ho solo trovato esempi di casting del tipo: int->float, float->double e mai il contrario.

Perciò, ipotizzando di avere:
float valore;
char stringa[dim];
...
valore= (float) atof(stringa);
.. 

oppure una funzione del tipo: (so che non ha senso questa funzione, ma è per rendere il "concetto" :oops: )
float funzione(char s[]){
..
return (float) atof(s)
}

Posso farli?

(2) Quando ho dei vettori la dimensione conviene scriverla come:
#define DIMENSIONE 10

oppure variabile:
const int DIMENSIONE=10


Sul libro spiega le due cose separatamente senza farne un confronto. Su internet, cercando, ho visto che molti lo hanno chiesto lo stesso e che le risposte si sono basate sul fatto che (in casi come questi di vettori) che define e const int si possono usare ugualmente ma non è sempre così visto che la define implica solo una sostituzione al momento della compilazione mentre la const int ha proprio un suo spazio in memoria.

Il dubbio è: posso veramente ignorare che son diversi ed usare una delle due, a scelta mia, per dichiarare la dimensione di un vettore? Cosa dovrebbe spingermi a scegliere? Inoltre, non capisco, la define allora non occupa memoria? Allora perché non usiamo sempre define e mai const int visto che sono "intercambiabili"?

Vi ringrazio anticipatamente per il vostro tempo ed aiuto!

Risposte
kobeilprofeta
Per quanto riguarda la conversione forzata. Sì. Penso tu possa fare tipo
f=(float)atof (s)


Riguardo al define : è sempre preferibile il define alla variabile costante; proprio perché non occupa memoria.
Però la variabile costante a volte fa cose che il define non può fare
Esempio (che mi era stato mostrato qua):
scanf ("%lf",&circonferenza );
scanf ("%lf",&raggio);
const double PI=circonferenza / raggio;

claudio862
"studente-studente":
posso forzare le variabili double ad essere di tipo float o rischio di perdere informazioni?

Il tipo double potrebbe essere più grande del tipo float, e quindi potrebbe rappresentare più valori. Ovviamente se una variabile di tipo double contiene un valore non rappresentabile come float, quando usi un cast (esplicito o implicito) perderai delle informazioni.
Ad esempio, sul mio sistema:

#include <stdio.h>

int main()
{
    double a = 1e200, b = 1.000000000001;
    float c = a, d = b;

    printf("a: %g\nc: %g\n", a, c);
    printf("b: %.16g\nd: %.16g\n", b, d);
    return 0;
}

ha come risultato

a: 1e+200
c: inf
b: 1.000000000001
d: 1


Il valore 1e200 è troppo grande per un float, quindi viene rappresentato come infinito. Il valore 1.000000000001 è troppo "preciso" per un float, quindi viene rappresentato come 1.

Leggi anche il famoso What Every Computer Scientist Should Know About Floating-Point Arithmetic.


"studente-studente":
posso veramente ignorare che son diversi ed usare una delle due, a scelta mia, per dichiarare la dimensione di un vettore? Cosa dovrebbe spingermi a scegliere? Inoltre, non capisco, la define allora non occupa memoria? Allora perché non usiamo sempre define e mai const int visto che sono "intercambiabili"?

#define implica una sostituzione "prima" della compilazione, durante la fase di preprocess. Un'implementazione potrebbe anche fare tutto nello stesso programma, ma i passaggi logici sono ben distinti. In teoria, il compilatore non vedrà mai nessun simbolo #, nessuna stringa "define", e nessuna stringa "DIMENSIONE". Vedrà solamente il numero 10.

In linea di massima dovresti usare #define il meno possibile, solo quando non hai alternative. Una costante ha un tipo e uno scope. #define sostituisce letteralmente una stringa con un numero dappertutto.
Una costante usa più memoria? Stiamo parlando di 4 byte. Inoltre il compilatore è abbastanza intelligente da fare lui la sostituzione, quindi in realtà sono 0 byte.

Quando hai dei "vettori" (intendi array?) diventa più complicato. Originariamente in C non avevi scelta, dovevi usare per forza #define. La dimensione di un array doveva essere una costante letterale (i.e., un numero). Nelle versioni più moderne sono stati introdotti array a dimensione variabile, ma sono in generale malvisti (è una incompatibilità con il C++, che non li supporta, in più non si possono prevenire errori quando la dimensione è troppo grande).
Due alternative sono:
- Usare una dimensione massima costante letterale abbastanza grande e segnalare un errore quando la si supera.
- Usare malloc() e compagnia per gestire dinamicamente la memoria.

studente_studente
Grazie mille per le vostre risposte, le ho viste il giorno stesso che le avete postate ma non ho avuto un attimo per rispondervi!

"claudio86":
In linea di massima dovresti usare #define il meno possibile, solo quando non hai alternative. [...]

Una costante usa più memoria? Stiamo parlando di 4 byte. Inoltre il compilatore è abbastanza intelligente da fare lui la sostituzione, quindi in realtà sono 0 byte.

Quando hai dei "vettori" (intendi array?) diventa più complicato. Originariamente in C non avevi scelta, dovevi usare per forza #define. La dimensione di un array doveva essere una costante letterale (i.e., un numero). [...]
Due alternative sono:
- Usare una dimensione massima costante letterale abbastanza grande e segnalare un errore quando la si supera.
- Usare malloc() e compagnia per gestire dinamicamente la memoria.


Sto ancora studiando ed apprendendo (chiedo venia) ma allora se la define non occupa completamente memoria perché dovrei preferire in ogni caso la costante che almeno per un secondo la occuperà? Supponendo di avere mille costanti da definire non mi converrebbe la define da questo punto di vista? Perché quel "usare il meno possibile", che ha di male la sostituzione? Non riesco a capire quali potrebbero essere le complicazioni dell'usare l'una o l'altra

Ho provato a pensare al alcuni motivi però (eheh):
1) potrebbe essere perché la define è obbligatoriamente valida ovunque, tipo una costante globale, mentre la una const non obbligatoriamente
2) se io dichiarassi un int e lo imponessi uguale ad un simbolo che con la define ho definito essere una stringa non farebbe errore, giusto? semplicemente tutto il programma va per conto suo.. la define non vuole i tipi come le const perché sostituisce senza controllare se sono giusti o meno
3) se devo definire 100 costanti con la define occupo 100 righe mentre con la const potenzialmente no perché basterebbe mettere solo una virgola (ammesso siano tutte lo stesso tipo)

apatriarca
Alcune ragioni per cui const è meglio di define:

1. define modifica ogni stringa uguale al nome della tua costante. Se definissi per sbaglio una variabile o una funzione o un tipo con quel nome otterresti quindi degli strani errori. Questo vale sia per gli oggetti da te definiti, sia quelli inclusi in librerie da te utilizzate.

2. Una variabile costante ha un indirizzo ed è quindi utilizzabile in situazioni in cui define non è utilizzabile.

3. Una variabile costante può essere definita a partire da altre variabili.

4. Gli errori di utilizzo di una variabile costante sono normalmente più semplici da comprendere (e quindi risolvere) che quelli basati su macro (il codice che il compilatore vede e su cui ti ha fornito l'errore è successivo alla preprocessazione).

5. La memoria usata da poche variabili costanti è comunque molto poco rispetto alla dimensione generale del programma. Inoltre non c'è una relazione così diretta tra variabili e memoria occupata. Il compilatore è in grado di riordinare operazioni, eliminare intere parti del codice, usare registri invece di spazio nello stack.. Se non vale la pena di memorizzare una variabile il compilatore è abbastanza bravo a non farlo.

In ogni caso, per le costanti usare delle macro va abbastanza bene. I problemi delle macro sono per lo più legati all'uso di parametri. Puoi cioè fare qualcosa come:
#define MAX(a, b) ((a) < (b) ? (b) : (a))

e scoprire che non funziona se chiamato nel seguente modo:
int max = MAX(a, b++);

Usando delle funzioni inline questo genere di problemi non si vengono a creare.

claudio862
"studente-studente":
Sto ancora studiando ed apprendendo (chiedo venia) ma allora se la define non occupa completamente memoria perché dovrei preferire in ogni caso la costante che almeno per un secondo la occuperà? Supponendo di avere mille costanti da definire non mi converrebbe la define da questo punto di vista? Perché quel "usare il meno possibile", che ha di male la sostituzione? Non riesco a capire quali potrebbero essere le complicazioni dell'usare l'una o l'altra

Perché queste sono cosiddette "micro-ottimizzazioni". Nella maggior parte dei casi non hanno nessun impatto sulle prestazioni. Il tuo obiettivo principale è scrivere codice leggibile, corretto e robusto. Quando (e se) avrai problemi di prestazioni, allora potrai preoccupartene.

Inoltre queste micro-ottimizzazioni sono spesso banali per il compilatore. Puoi fare una prova, prendi due programmi quasi uguali, dove l'unica differenza è const / define, e guarda il codice assembly generato dal compilatore. È uguale:

λ cat const.c                           │λ cat define.c
#include <stdio.h>                      │#include <stdio.h>
                                        │
int main()                              │int main()
{                                       │{
    const int N = 10;                   │#define N 10
                                        │
    int array[N];                       │    int array[N];
    array[4] = 3;                       │    array[4] = 3;
    printf("4: %d\n", array[4]);        │    printf("4: %d\n", array[4]);
    return 0;                           │    return 0;
}                                       │}
                                        │
λ cc -O2 -S -c const.c                  │λ cc -O2 -S -c define.c
                                        │
λ diff const.s define.s                 │λ
14c14                                   │
<       .file   "const.c"               │
---                                     │
>       .file   "define.c"              │
                                        │
λ                                


"studente-studente":
Ho provato a pensare al alcuni motivi però (eheh):
1) potrebbe essere perché la define è obbligatoriamente valida ovunque, tipo una costante globale, mentre la una const non obbligatoriamente

È peggio di una costante globale. Puoi avere una variabile locale con lo stesso nome di una variabile globale, la seconda viene "nascosta". #define sostituisce dappertutto. Non puoi avere due simboli con lo stesso nome.

"studente-studente":
2) se io dichiarassi un int e lo imponessi uguale ad un simbolo che con la define ho definito essere una stringa non farebbe errore, giusto? semplicemente tutto il programma va per conto suo.. la define non vuole i tipi come le const perché sostituisce senza controllare se sono giusti o meno

Il compilatore vedrebbe int a = "stringa";. Pensa anche cosa succederebbe se scrivessi int N = 4;.

"studente-studente":
3) se devo definire 100 costanti con la define occupo 100 righe mentre con la const potenzialmente no perché basterebbe mettere solo una virgola (ammesso siano tutte lo stesso tipo)

In ogni caso, dichiarare più di una variabile per linea è scoraggiato. È più leggibile avere una dichiarazione per linea.

studente_studente
"claudio86":

Il compilatore vedrebbe int a = "stringa";. Pensa anche cosa succederebbe se scrivessi int N = 4;..


Beh semplicemente nel primo caso darebbe un errore (oppure salva tutto a schifo sovrapponendo su altri spazi in memoria cose senza senso :?: )mentre nel secondo no.. credo

"claudio86":

In ogni caso, dichiarare più di una variabile per linea è scoraggiato. È più leggibile avere una dichiarazione per linea.

E' più leggibile ma è addirittura scoraggiato?
Certo se ho 3k dati di tipi vari forse non è il massimo della leggibilità ma facendo una dichiarazione per riga si ha continuamente padding sprecando un sacco di spazio pur di garantire l'allineamento dei tipi. (sempre supponendo di non usare nessun tipo di struttura dati opportuna, giusto qualcosa del tipo int x, char z, float w);

"apatriarca":

#define MAX(a, b) ((a) < (b) ? (b) : (a))

e scoprire che non funziona se chiamato nel seguente modo:
int max = MAX(a, b++);



Vabbè ma questo perché define è una macro non una funzione quindi non può farlo in ogni caso.. il programma sostituisce ovunque ci sia MAX(a,b) l'espressione ((a) < (b) ? (b) : (a)).. gli eventuali errori dovrebbero esserci nel momento in cui su int max metto quella cosa che non ha senso senza un if e due variabili/costanti a e b dichiarate prima no? :?:

P.S. Non ho ancora visto le funzioni inline, ho dato un'occhiata online ma ho notato che sono parte del C++ quindi rimando al futuro un loro approfondimento!

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.