Newsgroup: it.comp.lang.c++
Titolo: AIUTATEMI! VOGLIO CAPIRCI QUALCOSA!m (C/C++)

Ciao, mi chiamo Davide e premetto che sono alle prese col C/C++ da
solo qualche giorno, studiando su un manuale senza nessun supporto di
qualcuno che mi spieghi le cose, quindi spero che mi capiate se chiedo
consiglio....
Ho capito abbastanza bene come funzionano gli array e i puntatori, ma
non sono riuscito a capire una cosa del seguente codice (da es. del 
manuale sopra citato...). Dato il seguente codice:
#define MAX 100
#define LUNG 80
char testo[MAX][LUNG];
/* legge stringhe dalla tastiera e le stampa */
void main()
{
  register int t, j, i;
  for(t=0; t<MAX; t++){
    printf("%d: ", t);
    gets(testo[t]);
    if(!*testo[t]) break; /* salta le linee vuote */
  }
  for (i=0; i<t; i++){
    for(j=0; testo[i][j]; j++) putchar(testo[i][j]);
    putchar('\n');
  }
}
(spero non ci siano errori di battitura nel codice!)
Quello che no riesco a capire, è il perchè nella condizione IF per
il controllo delle linee vuote, sia stato posto * prima dell'array
di stringhe (effettivamente ho provato a toglierlo e il programma non
riconosce mai le linee vuote...).Ho capito che serve per indicare il
CONTENUTO dell'array, ma allora che differenza c'è tra questa condizione
e quella dell'ultimo ciclo FOR del codice, nel quale è stato messo il
nome dell'array senza * ? Entrambi devono controllare che il CONTENUTO
dell'array non sia nullo................
(Forse sono stato un poco contorto, sper di no....)
Sarei grato a chiunque potesse illuminarmi su questo particolare senza
il quale non mi sento di proseguire gli studi!
Grazie in anticipo e ciao.

 riconosce mai le linee vuote...).
Fare in questo modo 'if(!*test[t]) ....'
o in questo modo 'if(test[t]==0)...'
e' la stessa cosa.
Io personalmente preferisco la seconda tecnica perche' piu' chiara
soprattuto per coloro che come te non conoscono ancora bene il C.
--
ciao
===============================================
Marco Maroni  
m.ma...@esa-group.it
Analista/Programmatore
-----------------------------------------------
ESA Software S.p.A. 
http://www.esa-group.it
via Flaminia, 235/AX tel. +39 541 368111
47037 Rimini (ITALY) fax +39 541 368268
===============================================
--------------470012EC5E4516453813B8F9
Content-Type: text/html; charset=us-ascii
Content-Transfer-Encoding: 7bit
<HTML>
<TT>Davide Ravaglia wrote:</TT>
<BLOCKQUOTE TYPE=CITE><TT>void main()</TT>
<BR><TT>{</TT>
<BR><TT>&nbsp; register int t, j, i;</TT>
<BR><TT>&nbsp; for(t=0; t&lt;MAX; t++){</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; printf("%d: ", t);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; gets(testo[t]);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; if(!*testo[t]) break; /* salta le linee vuote
*/</TT>
<BR><TT>&nbsp; }</TT>
<BR><TT>&nbsp; for (i=0; i&lt;t; i++){</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; for(j=0; testo[i][j]; j++) putchar(testo[i][j]);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; putchar('\n');</TT>
<BR><TT>&nbsp; }</TT>
<BR><TT>}</TT>
<P><TT>(spero non ci siano errori di battitura nel codice!)</TT>
<P><TT>Quello che no riesco a capire, &egrave; il perch&egrave; nella condizione
IF per</TT>
<BR><TT>il controllo delle linee vuote, sia stato posto * prima dell'array</TT>
<BR><TT>di stringhe (effettivamente ho provato a toglierlo e il programma
non</TT>
<BR><TT>riconosce mai le linee vuote...).</TT></BLOCKQUOTE>
<TT>Fare in questo modo 'if(!*test[t]) ....'</TT>
<BR><TT>o in questo modo 'if(test[t]==0)...'</TT>
<BR><TT>e' la stessa cosa.</TT>
<BR><TT>Io personalmente preferisco la seconda tecnica perche' piu' chiara
soprattuto per coloro che come te non conoscono ancora bene il C.</TT>
<P><TT>--</TT>
<BR><TT>ciao</TT>
<BR><TT>===============================================</TT>
<BR><TT>Marco Maroni&nbsp; 
m.ma...@esa-group.it
</TT>
<BR><TT>Analista/Programmatore</TT>
<BR><TT>-----------------------------------------------</TT>
<BR><TT>ESA Software S.p.A. <A HREF="
http://www.esa-group.it
">
http://www.esa-group.it
</A></TT>
<BR><TT>via Flaminia, 235/AX tel. +39 541 368111</TT>
<BR><TT>47037 Rimini (ITALY) fax +39 541 368268</TT>
<BR><TT>===============================================</TT></HTML>
--------------470012EC5E4516453813B8F9--

: Davide Ravaglia wrote:
: Fare in questo modo 'if(!*test[t]) ....'
: o in questo modo 'if(test[t]==0)...'
: e' la stessa cosa.
Per niente, semanticamente sono due cose molto diverse. Nel primo caso
controlli il valore di un char, nel secondo confronti due puntatori.
Difatti il secondo metodo non funziona, forse andra` bene sul tuo compilatore,
ma secondo le regole del C++ i due test non sono equivalenti.

keyword const.
Dissento !!! W il preprocessore !! :)
Non vorrei innescare una futile polemica, ma mi pare che a volte si
tenda un po' troppo ad esagerare nel consigliare l'utilizzo di
variabili const al posto delle banali #define. 
Gianpaolo Bottin
- Live Long and Prosper -

dell'array non sia nullo................
Le due righe sono equivalenti.
Per come il C tratta array e puntatori e' sempre possibile utilizzare
un doppio sistema per accedere agli array, quello piu' tradizionale
che fa uso delle parentesi quadre e quello tipico del C che utilizza
l'operatore *
Ad esempio la riga
    if(!*testo[t]) break; /* salta le linee vuote */
si sarebbe potuta scrivere anche cosi':
    if(!testo[t][0]) break; /* salta le linee vuote */
Mentre il FOR si poteva scrivere in modo piu' elegante cosi':
  for (i=0; i<t; i++)
	{
	char *pTxt=&testo[i][0];
	while (*pTxt) putchar(*pTxt++);
	putchar('\n');


Andrea Ferro (And...@SWAN.it) wrote:
: Ho visto un paio di reply al tuo messaggio, ma uno dei due =E8 =
: sbagliato e
: l'altro tremendamene impreciso.
Che il mio replay (presumo sia il mio) sia impreciso e` una possibilita`
(non sono perfetto), comunque questo tuo e` lo e` sicuramente.
Ma andiamo con ordine...
: Cominciamo con il notare che il codice presentato =E8 codice C e non
: codice C++ poiche' non sfrutta nessuna caratteristica del C++
E` vero, comunque l'autore del primo messaggio parlava anche di C++ ed
essendo questo un newsgroup dedicato al C++...
In ogni caso ormai la keyword const fa parte dell'ansi C e quindi non
sarebbe male utilizzarla al posto delle deprecate #define.
e anzi un
: moderno compilatore C++ probabilmente si arrabbier=E0 perche' il main =
: =E8
: dichiarato void.
Questa e` gia` una sciocchezza: il C++ non pone vincoli sui tipi coinvolti
nel prototipo di main(), i limiti sono di altro tipo (es. main non
puo` essere ricorsiva o static). Testualmente B. Stroustrup dice
"This function is not predefined by the compiler, it cannot be
overloaded, and its type is implementation dependent" nel seguito
l'autore si raccomanda su due prototipi che dovrebbero essere sempre
supportati.
Non c'e` quindi motivo per ritenere errato dichiarare void la funzione
main() anche se il compilatore non l'accetta (il motivo puo` e in genere lo
e`, essere legato al sistema operativo).
: Detto questo passiamo a parlare dei puntatori e degli array in C (ma la
: stessa cosa vale a tutti gli effetti anche in C++).
: La dichiarazione =E8:
: 	char testo[100][80];
: Queso significa che testo =E8 un vettore di 100 vettori di 80 caratteri
: l'uno. Non ci sono puntatori.
Solo apparentemente!
: Ora passiamo ad analizzare l'operatore []. Per definizione l'operatore
: [] va interpretato cosi:
: 	Exp1[exp2]    corrisponde a    *(exp1+exp2)
Ok
: Ora veniamo alla tanto malinterpretata confusione fra array e =
: puntatori.
: Per permettere di usare l'operatore [] come viene ovvio usarlo =E8 =
La cosa non e` per niente ovvia, si tratta semplicemente di una scelta
di design, se il concetto di array e quello di puntatore fossero stati
nettamente separati, la cosa sarebbe cambieata radicalmente.
: stata
: introdotta la regola che un riferimento ad vettore di n elementi =
: diviene
: un l-value corrispondente al puntatore al primo elemento.
in parole povere in C e in C++ un T[] puo` essere convertito in T*
: Classifichiamo i puntatori come puntatori ad un tipo ben definito. E
: sappiamo che un tipo ha una dimensione (sizeof). Possiamo indicare, in
: generale, un puntatore utilizzando la dimensione del tipo puntato.
: Questo valore corrisponde all'incremento ottenuto con l'aritmetica dei
: puntatori. Per esempio:
: 	char *pc;
: 	int *pi;
: =E8 evidentemente che pc =E8 un puntatore ad un carattere mentre pi =E8 =
: un
: puntatore ad un intero. Utilizziamo il simbolo ^ per indicare un genere
: di indirizzo contenuto in un puntatore in base alla dimensione
: dell'oggetto indirizzato. Diremo quindi che pc contiene un indirizzo ^1
: mentre pl contiene indirizzo ^2 (supponiamo di stare a 16 bit). 
E` sbagliato fare supposizioni circa la dimensione dei tipi, in quanto
dipendente dall'implementazione. Due implementazioni possono differire
anche se si opera a 16 bit, lo standard (o se preferisci il linguaggio)
non dice quale debba essere la dimensione di ciascun tipo, ma quale
intervallo di valori debba essera possibile rappresentare.
Non capisco comunque cosa abbia a che fare questo discorso col nostro
problema.
:Notiamo
: che pc+=3D2 incrementa l'indirizzo contenuto in di pc di due unit=E0, =
: mentre
: pl+=3D2 lo incrementa di quattro unit=E0.
: Passiamo ora ad alcune dichiarazioni e espressioni che ci chiariscano
: cosa =E8 un vettore, cosa =E8 un puntatore e come un vettore assuma il
: valore di un indirizzo:
: Le dichiarazioni sono:
: 	int v;
: 	int *p;
: 	int a[4];
: che dichiarano rispettivamente una variabile v contenente un intero, un
: puntatore p contenente l'indirizzo di un intero (un indirizzo ^4) e un
: array a di quattro interi. Supponiamo di stare in un sistema a 16 bit
: con indirizzi lunghi (il vecchio modello far del dos, per intenderci).
: Abbiamo quindi sizeof(int) =3D 2 e sizeof(void*) =3D 4.
: Ecco alcune espressioni, il loro valore ed il loro tipo:
: 	Espr.  rappresenta                 valore
: 	v      variabile intera            un numero intero
: 	&v     indirizzo di un intero      un indirizzo ^2
: 	p      puntatore a intero          un indirizzo ^2
: 	&p     indirizzo di un puntatore   un indirizzo ^4
: 	*p     un intero                   un numero intero
: 	a      vettore di due interi       un indirizzo ^2 (del primo
: intero)
: 	&a     indirizzo di un vettore     un indirizzo ^8
: Questo dovrebbe rendere chiara la differenza fra a e p, che non sono
: affatto la stessa cosa.
: La regola del valore di un array (penultima riga) crea non pochi
: problemi in espressioni come queste:
: 	a[2]
: 	*a
: dove la a viene prima convertita nel suo valore e POI viene applicato
: l'operatore. Quindi in *a l'operatore * non viene applicato ad a ma al
: suo valore.
: Tornando al tuo codice l'espressione nell'if =E8 bruttissima ma =
: funziona
: perch=E8 testo[t] rappresenta un vettore di 80 caratteri il quale =
: decade
meglio dire "viene convertito"
: in un puntatore ad un carattere al quale viene applicato l'operatore *.
sbagliato:
poiche la dichiarazione e` char testo[100][80],
testo[t] viene convertito in char *[] (per via della regola di conversione),
cioe` un puntatore ad un vettore
di char, solo che poiche` un array multidimensionale viene memorizzato
linearmente, di fatto l'espressione testo[t] punta ad un carattere.
: L'espressione "!*testo[t]" corrisponde a "!*(&testo[t][0])" ovvero a
: "!testo[t][0]" o se preferisci a "testo[t][0]=3D=3D0". =C9 sbagliato =
: quello
: che =E8 stato detto nell'altro reply, ovvero "testo[t]=3D=3D0" poch=E8 =
: questo
: paragona l'indirizzo del primo elemento del vettore a 0, non l'elemento
: in se ma il suo indirizzo. E non =E8 neppure giusto (come si diceva nel
: secondo reply) che questo =E8 un paragone fra puntatori.
E invece lo e`: poiche il primo operando dell'operatore == e` un indirizzo
(o equivalentemente un puntatore) il compilatore converte 0 al puntatore
nullo.
: E' un paragone
: fra indirizzi, ma non ci sono puntatori coinvolti.
Sebbene un puntatore sia una variabile che contiene indirizzi, poiche`
il confronto di due puntatori si traduce in un confronto di due
indirizzi, dire che si confrontano due puntatori o due indirizzi e`
la stessa cosa.
Comunque per ogni evenienza consiglio la lettura approfondita di
"The C++ programming language"2 edizione di B. Stroustrup
edito da Addison Wesley (vivamente consigliata la versione in inglese)
Saluti,
Paolo

Gianpaolo Bottin (
bott...@tin.it
) wrote:
: 
mar...@cli.di.unipi.it
 (Paolo Marotta) ha scritto:
: Dissento !!! W il preprocessore !! :)
: Non vorrei innescare una futile polemica, ma mi pare che a volte si
: tenda un po' troppo ad esagerare nel consigliare l'utilizzo di
: variabili const al posto delle banali #define. 
L'uso di #define crea diversi problemi:
1) poiche` #define e` gestita dal preprocessore, non e` possibile eseguire
   controlli semantici di scope e tipo. Cio` puo` causare problemi di
   portabilita` (anche subdoli) es:
   #define costante 65000
   int f() { return costante; }
   questa funzione funziona su macchine a 32 bit, ma non su macchine a
   16 bit. L'uso di cost avrebbe causato quanto meno una warning!
2) se per caso scrivi
    #define X 65;
   il punto e virgola finale ti causera` sicuramente grossi problemi in
   fase di compilazione e stai sicuro che nessun compilatore sara` in
   grado di farti capire dove si trova il problema (l'errore verra`
   segnalato sulla riga dove X e` stato espanso, non sulla #define).
3) Le costanti #define non sono visibili (normalmente) nel debugging
   con debugger simbolici
Ci sarebbe almeno un altra fonte di errore, ma queste credo bastino per
tagliare la testa al toro!
Paolo
: Gianpaolo Bottin


tagliare la testa al toro!
Non ne sono ancora convinto, le vecchie abitudini sono dure a morire.
:)
Spesso pero' e' possibile usare le enum come valida alternativa alle
#define senza dover per forza allocare memoria per una variabile.


Gianpaolo Bottin (
bott...@tin.it
) wrote:
: 
mar...@cli.di.unipi.it
 (Paolo Marotta) ha scritto:
: anche in questo caso il compilatore dovrebbe darti un warning, infatti
: il preprocessore espande la costante come 65000, quando poi il
: compilatore genera il codice "dovrebbe" accorgersi che la costante e'
: troppo grande. Al di la' di questo esempio comunque il tuo appunto e'
: valido, soprattutto quando si tratta di macro usate come funzioni
: inline.
Effettivamente di solito un compilatore da una warning, ma in ogni
caso quando vai a fare il porting sei costretto a modificare il codice,
tutto questo per aver utilizzato la #define
: ok, questo e' un errore di programmazione abbastanza comune quando si
: inizia a programmare in C, come quello di mettere = invece di ==
Non e` un buon motivo per mantenere una possibile fonte di errore
+bud = +tempo = maggiori costi!
: allora che facciamo eliminiamo anche l'operatore = ? :)
Che c'entra "? :" ?
: fortunatamente alcuni debugger visualizzano anche le #define.
E se poi non trovi un tale debugger per la piattaforma su cui esegui il
porting?
: >Ci sarebbe almeno un altra fonte di errore, ma queste credo bastino per
: >tagliare la testa al toro!
Ecco l'altra fonte:
#DEFINE PI = 3.141
#DEFINE 2PI = PI + PI
float Circonferenza(float raggio) {
  return (raggio * 2PI)		// Errore! Capito il perche`?
}
: Spesso pero' e' possibile usare le enum come valida alternativa alle
: #define senza dover per forza allocare memoria per una variabile.
Non e` detto che il compilatore allochi memoria per le costanti definite con
const. Questo e` vero solo se cerchi di ottenere l'indirizzo della costante
oppure per un oggetto const.
Paolo


      ________
      \ \ \ \ \    __
       | | | | |  / _C
 __   /_/_/_/_/   \ \
 \ `-()-()-()-()-'  /
 ~~~~~~~~~~~~~~~~~~~~
... Hiroshima '45, Tjernobil '86, Windows '95





Marco V. Sandrini (sat...@usa.net.REMOVETHIS) wrote:
: Il mondo ti sorride poco, pero', perche' un nome di identificatore non
: si comincia con una cifra!!! L'identificatore "2PI" basta da solo a
: generare errore.
D'accordo, tuttavia cio` non toglie nulla all'esempio, quanto alla
mancanza delle parentesi, la cosa era volontaria un programmatore
esperto (che probabilmente sara` incappato nel problema) avrebbe sicuramente
utilizzato le parentesi...
: Ricapitolando:
: #define PI        3.141
: #define DOUBLEPI  (PI + PI)
: float Circonferenza(float raggio) {
:   return (raggio * DOUBLEPI);
: }
: E questa volta non ci sono piu' errori.
: In ogni caso io sono un sostenitore delle #define, ben cosciente che
: bisogna organizzarle con criterio. Ad esempio sono l'ideale da usare
: come maschere sui bit. Ci saranno poi altri casi in cui e' meglio una
: costante.
Ma non c'e` alcun motivo per non utilizzare le const per maschere di bit,
come gia` detto per tipi primitivi i compilatori non allocano mai
memoria (a meno che non sia ad esempio richiesto l'indirizzo della
costante).
Per concludere il discorso, non vedo perche` utilizzare le define e
preoccuparsi di evitare errori se con le const puo` pensarci il compilatore,
le define non danno alcun vantaggio.
: Ad esempio in questo caso del P greco io avrei scelto senza esitazione
: un numero in virgola mobile, e l'avrei scritto per intero:
: const float PI=3.1415927;
Questi sono solo dettagli che non era necessario includere nell'esempio.
Saluti,
Paolo

oppure per un oggetto const.
ne sei proprio sicuro ? Sulla language refence del Visual C++ ho letto
che i valori const in C++ hanno di default solo il link interno
(traduzione di "default to internal linkage") pero' nulla viene
specificato sul fatto di allocare o meno della memoria. Ho comunque
perso un po' di tempo a fare qualche prova, il risultato e' che il
Visual C++ 5.0 alloca memoria per i valori const. Si puo' verificare
sia dalle dimensioni del file eseguibile sia analizzando il sorgente
assembly generato dal compilatore. Quindi se si usa un vlore const al
posto di una #define il buon VC alloca una variabile per OGNI modulo
che che usa quella costante, non solo ma il codice generato e' anche
leggermente piu' grande, infatti per chiamare la funzione test(C0)
dove C0 e' una costante double il vc genera il seguente codice:
        mov     ecx, DWORD PTR _C0$S47680+4
        mov     edx, DWORD PTR _C0$S47680
        push    ecx
        push    edx
        call    ?test@@YAXN@Z           
con una #define invece:
        push    1072693248                              ; 3ff00000H
        push    0
        call    ?test@@YAXN@Z                           ; test
penso proprio che continuero' ad usare le #define.



Fido: Nyurl, 2:331/303.2


Federico Fissore (
fi...@dpdieci.well.it
) wrote:
: Cosa intendi per tipi primitivi ? Io ho sempre saputo che le const non
: erano altro che
: variabili a valore costante e che pertanto occupavano la stessa memoria che
: occupava
: una normale variabile. Io mi aggrego a chi sostiene le #define: non sono il
: più facile sistema ma sono sicuramente il più utile e il più vantaggioso ai
: fini della programmazione pulita e leggera. 
Dunque...
i tipi primitivi sono quelli predefiniti del linguaggio (char, int, float e
double) con eventualmente dei modificatori (unsigned, signed, short e long).
Quanto all'allocazione di memoria, questo e` vero solo per alcuni compilatori;
formalmente una const e` una variabile a sola lettura (o almeno all'inizio
cosi` fu definita), ma se non si accede ad essa in scrittura (se e` const
non ci dovrebbero essere motivi validi) o comunque non si fanno
operazioni che potrebbero in casi estremi consentirne la modifica, non c'e`
motivo per allocare memoria (e difatti, almeno il mio compilatore non alloca
memoria). Questo comunque e` vero solo per i tipi predefiniti (e comunque
con le #define non mi risulta sia possibile avere costanti non memorizzabili
in tipi elementari), per i tipi derivati la questione e` talmente complessa
che e` meglio allocare memoria.
Premesso che rispetto, ma non condivido, opinioni favorevoli alle
#define... ho molti dubbi sulla programmazione "pulita e leggera" che si
ottiene con le #define. Se un giorno farete il porting su altre piattaforme
di codice che fa massiccio uso delle define, vi renderete conto dei grossi
problemi che possono causare e molto probabilmente dovrete pesantemente
ritoccare il codice aggiungendo parecche conversioni esplicite di tipo
(come fanno i programmatori C, visto che tale linguaggio non ha un sistema
dei tipi).
Saluti,
Paolo