Newsgroup: it.comp.lang.c
Titolo: come prendo da tastiera una elemento di variabile strutturata?

ciao. Seguendo la domanda in oggetto al post...
se la variabile č per esempio:
struct computer {
char *marca
char *modello
}
quindi dichiaro prima della main() un
struct computer casa
poi nella main, io seguo l'esempio del mio libro su un caso simile, e
scrivo:
gets(casa.marca);
Ma provato in un mio semplice programma, non funziona e oltretutto
questo gets č la prima volta che "spunta" nelle pagine lette, senza
dare tante spiegazioni. Non si puņ acquisire la variabile con il
classico scanf ("%s"...  ?
Grazie

 gets(casa.marca);
E vai in undefined behaviour.
Qual'e' il problema? Quel che fa la gets (che tra l'altro consiglio di 
sostituire con l'uso di fgets, per motivi di buffer overflow che sono 
gia' stati discussi in passato piu' volte su questo newsgroup) e' di 
mettere i dati che vengono inseriti da tastiera all'indirizzo di memoria 
che gli e' passato. Punto e basta, senza alcuna interpretazione dei dati 
come fa la scanf. Che indirizzo gli hai passato tu? Spazzatura, visto 
che il campo casa.marca non e' stato inizializzato. Il risultato e' 
quindi che i dati inseriti da tastiera andranno a finire... chissa' dove!
Comunque: spiegare meglio cosa significa "non va" sarebbe meglio, e 
dubito fortemente che il tuo libro usi una funzione senza spiegarla 
prima o immediatamente dopo.
-- 
Mat/tia Belletti - Undergraduate student @ 
cs.unibo.it
ICQ: 33292311    - email: 
mbel...@cs.unibo.it
IRC: RedGlow     - site(s): 
http://mbellett.web.cs.unibo.it/
Jabber: RedGlow  - Linux registered user 299762 @ machine 213003

 classico scanf ("%s"...  ?
riporto dalla mia guida:
char *gets(char *s);
Gets a string from stdin.
gets collects a string of characters terminated by a new line from the 
standard input stream stdin and puts it into s. The new line is replaced 
by a null character (\0) in s.
On success, gets returns the string argument s.
On end-of-file or error, it returns NULL
ciao.

 classico scanf ("%s"...  ?
Si puň anche cosě. Funzionalmente gets(ptr) č analogo a
scanf("%s", ptr).

 scanf("%s", ptr).
AAA. cedesi scontata vulnerabiltà da buffer overlflow causa inutilizzo
-- 
"le funzioni dovrebbero funzionare" -- muco
sbi

 scanf("%s", ptr).
A meno che io non capisca cosa intendi tu per "Funzionalmente" o che io non
ne sappia il significato preciso, pero` gets( ptr ) è analogo a scanf( "%s",
ptr ) unicamente se la stringa e` composta da una sola parola...
Tuttalpiu` e` analoga a scanf( "%[^\n]", ptr )
(poi ci sono altre differenze di natura prestazionali...)

 quindi che i dati inseriti da tastiera andranno a finire... chissa' dove!
OK. Provo allora in un programma (che inserisco sotto).
Ho provato (adesso ho un puntatore p che punat dove è la variabile
duemila) ma ho un errore a linea 7: error: two or more data types in
declaration of `duemila'
Non riesco ad andare avanti, nonostante i vostri suggerimenti. Per
favore, cosa sbaglio?
#include <stdio.h>
struct automobili {
char *marca;
char *modello;
int vendute;
	}
struct automobili duemila;
int main()
{
char *p;
p=&duemila;
printf("Marca?");
gets(*p.marca);
scanf("%s",duemila.marca);
}

 ne sappia il significato preciso, 
Forse č sul significato di "analogo" su cui bisogna disquisire, per me 
non č sinonimo di "uguale" o "equivalente" ;-)

 struct automobili duemila;
...
se vuoi dichiarare la variabile duemila del tipo "struct automobili" 
nella stessa istruzione in cui definisci il tipo dati puoi combinare le 
due istruzioni cosi' :
  struct automobili
    {
      char *marca;
      char *modello;
      int vendute;
    }
     duemila;
Attenzione poi a come usi il puntatore: se lo fai puntare alla struttura 
devi dichiarare un puntatore a *quel* tipo dati (struct automobili) e 
non al tipo dati dei singoli campi.
Giorgio

...

OK Giorgio, grazie. Ho fatto come dici.
Ho ancora due errori( il primo, 'char *' differs in levels of
indirection from 'char ') nel compilatore (che scopro piu semplice con
visual c, che farlo da shell in linux, come h fatto prima). Sapessi
cosa volessero che corregga, non sarei di nuovo qui a domandare alla
vostra pazienza il perchč il compilatore si ferma...
#include <stdio.h>
struct automobili {
char *marca;
char *modello;
int vendute;
	}
duemila;
int main()
{
struct automobili *p;
p=&duemila;
printf("Marca?");
gets(*p->marca);
scanf("%s",duemila.marca);
}

 }
Allora, vediamo di riprovare a spiegartelo, visto che hai rifatto 
l'errore pari a prima. Parlero' a basso livello - cosa che in un ng di 
standard non si dovrebbe fare - ma credo aiuti a capire.
Cos'e' un un puntatore? Un numero, che rappresenta l'indirizzo in 
memoria della variabile che indicano. Diciamo che dentro ci sia scritto 
1000000? Allora vuol dire che la variabile a cui punta e' alla locazione 
di memoria 1000000. Nei nostri esempi prenderemo tutti i numeri lunghi 4 
byte.
Tu cosa fai nel programma? Hai inanzitutto una variabile globale 
'duemila'. Poi, nel main, metti nello stack un puntatore ad 
'automobili'. Fai si' che punti a 'duemila', quindi a questo punto in 
'p' c'e' l'indirizzo di 'duemila'. Dato che dopo non sfrutti mai questo 
fatto, ma dereferenzi semplicemente 'p' e bona, potevi anche saltarlo 
questo passaggio, e non si capisce il perche' l'hai fatto. Ma comunque.
Quindi, prendi "*p->marca". Allora: '*p' e' sostanzialmente 'duemila', 
quindi no problem. Quindi e' come dire 'duemila.marca'. Bene, quanto 
vale il campo 'marca'? 'duemila' e' una variabile globale, che non hai 
inizializzato. Ergo, ci sono dentro dati a caso (molto probabilmente 
degli 0). Prendiamo che ci siano dentro degli 0. Allora duemila.marca e' 
0, duemila.modello e' 0, duemila.vendute e' 0.
Quindi, stai dicendo alla gets: "leggi una stringa e salvala alla 
locazione di memoria 0". Son pure convinto che qualcosa non funzioni, 
pover gets!
Il problema e' tipico di chi inizia a programmare in C. Un puntatore e 
una array sono MOLTO differenti, nonostante quello che puo' sembrare (e 
quindi, allo stesso modo, un puntatore e una stringa): un array e' una 
zona di memoria su cui puoi lavorare direttamente, un puntatore 
semplicemente _ci punta_, e se da qualche parte quel qualcosa non punta 
a una vera zona di memoria, vai a scrivere in zone a caso!!
Arriviamo alla soluzione: o al posto di usare puntatori, usi 
direttamente array, oppure allochi dinamicamente buffer in memoria con 
malloc - questo proprio per avere della memoria su cui lavorare. In 
entrambi i casi, ripeto: gets e' pericolosa, usa fgets.
E comunque, togli quel "p" che, come avrai intuito, e' inutile.
Spero che tutto questo discorso "a basso livello" ti abbia aiutato. La 
sostanza e': la coerenza dei tipi e' necessaria per un programma 
corretto, ma non sufficiente (fosse cosi', potrebbero scrivere programmi 
direttamente i computer ;P), e "forzarla" a tentativi ovviamente e' 
inutile e rischioso - e' necessario capirne il vero significato.


 Grazie
L'errore fondamentale per cui, con il codice che hai scritto, non 
riuscirai mai ad acquisire una stringa, è dovuto al fatto che hai 
dichiarato due campi char* che non puntano a nessun indirizzo di memoria 
utilizzabile (allocata). E questo ti era già stato detto in precedenza.
Quindi hai a disposizione almeno 2 possibilità:
1. O dichiari la struct più o meno così:
  struct computer {
  char marca[XXX]
  char modello[YYY]
  }
dove XXX e YYY sono due interi (a questo punto ovviamente potrai 
memorizzare solo stringhe di lunghezza massima pari a XXX-1 e YYY-1), e 
quindi la gets (o la scanf) potrebbero funzionare (con la limitazione 
nel numero dei caratteri). In ogni caso ti consiglio di usare una 
stringa temporanea sovradimensionata, per controllare che effettibamente 
il numero di caratteri non siano troppi.
2. Non modifichi la dichiarazione della struct, ma allochi della memoria 
per i due valori della struct, con del codice tipo
duemila.marca = (char *) malloc (sizeof(char)*XXX)
dove XXX è come sopra. La differenza fondamentale sta nel fatto che non 
sei più obbligato a dimensionare nello stesso modo tutti i campi "marca".
Io farei così:
       - Crei una stringa temporanea abbastanza grande
       - Acquisisci il testo in quella stringa
       - Controlli quanto è lunga realmente la stringa acquisita
       - Allochi tanta memoria quanta ne serve, né + né -
       - Copi la stringa temporanea nella struct
Ulteriori problemi si pongono nel caso tu non sappia quanto sarà grande 
(al massimo) la stringa, ma questo è un altro problema.
PsychoWood

 standard non si dovrebbe fare - ma credo aiuti a capire.
Grazie della spiegazione, quando sono off line me la leggo e me la
studio.
Ma perchč non di dovrebbe parlare a passo livello in questo NG?
Io sono in questo momento a Ginevra poi in Africa ad insegnare
informatica (il C e' compreso nel programma) ... se non avessi questo
NG che mi fa piano piano progredire nel capire gli esercizi dei miei
libri, ......non saprei proprio come fare. Per quello che mi riguarda
benvengano spiegazioni a basso livello.
Se ci sono altri luoghi nel web dove poterne parlare anche a livelli
bassi, come ora dici e' la tua spiegazione, mi puoi indicare ? Non
abbandonerei questo NG, ma magari avrei piu risorse a disposizione (e
non guide o tutorial...quelli li ho, parlo proprio di interagire con
qualcuno che ti spiega meglio "cosi' si fa, cosi' no".

 standard non si dovrebbe fare - ma credo aiuti a capire.
Devo dire che questa tua premessa, e in particolare la definizione "un NG di
standard" m'ha lasciato un po' perplesso. Francamente, non è così che io
intendo il topic di questo NG.
Correggetemi se sbaglio ma io direi che questo è un NG sul linguaggio C
standard, non sullo standard del linguaggio C...
Detto così pare un gioco di parole ma non lo è: intendo dire che questo non
è un gruppo per sviluppatori di compilatori di C (o aspiranti tali), ma per
*utenti* del linguaggio C (o aspiranti tali).
Mi pare perfettamente IT che gente che sta imparando il C chieda perché una
certa cosa non funziona come s'aspetta, e gente che sa bene il C che cerchi
di spiegarglielo.
Se per fare questa spiegazione è funzionale scendere a basso livello, come
hai fatto tu, cioè cercare di immaginare e spiegare quel che succede al
momento dell'esecuzione, non mi pare che ci sia niente di OT, anzi.
A mio avviso, uno non ha capito veramente il C (o qualsiasi altro linguaggio
compilato, del resto) se non riesce a immaginare almeno a grandi linee come
un programma verrà tradotto in linguaggio macchina.
IMHO.
--
Cingar
--------------------------------
Inviato via 
http://arianna.libero.it/usenet/

Rispondo qui sia a Cingar che Nicola:
d'accordo, questo e' un newsgroup per utenti, PRINCIPALMENTE. In 
realta', e' semplicemente un newsgroup dove si discute del C standard - 
che tu sia un programmatore C, uno sviluppatore di compilatori, poco 
importa.
Certo, puo' essere funzionale un esempio a basso livello. Rimane il 
fatto che lo standard spiega quelle cose in modo diverso. Ora, per una 
volta, magari, parlare a basso livello puo' chiarire di piu' le idee, ma 
mi sentivo in dovere di avvertire - questo perche' non stavo dicendo 
cose vere in assoluto, ma stavo solo facendo un esempio, e se avessi 
voluto essere pedante non avrei dovuto fare questo, ma piuttosto avrei 
dovuto parafrasare lo standard, solo in modo piu' chiaro.
Per concludere: per avere spiegazioni "a basso livello" devi 
specializzarti sulla piattaforma sulla quale ti muovi, e questo 
comprende un insieme di compilatore, sistema operativo, architettura 
hardware, etc...
Solitamente, per i newsgroup, basta riferirsi all'SO: 
it.comp.programmare.win32 e it.comp.os.linux.development, per fare due 
esempi.


 fgets. 
Uso adesso fgets. Ma la storia del malloc, la rimando, non è un
concetto per nulla chiaro e preferisco uscire gia dalle paludi
dei puntatori, delle funzioni e delle variabili strutturate.
-- 
Inviato da Virgilio 
http://newsgroup.virgilio.it
..un servizio di news via web con in piu' l'antispam

 Diverso è se scrivo char *animale="gatto" e vado sul sicuro  ?
Si', in questo caso vai sul sicuro. Noterella a margine, che non c'entra 
propriamente con l'argomento: ricordati che in questo caso 'animale' 
punterebbe a una stringa _non_ modificabile! :-)
