Newsgroup: it.comp.lang.visual-basic
Titolo: Bachetto ?

Ciao a tutti.
Ho perso un po' di tempo per una strana cosa
che non avrei immaginato con VB6.  A me
sembra si tratti di un baco, ma vorrei il vostro
illuminato parere.   Per verificarlo bisogna creare
un nuovo prj e fare un cut & paste del codice
qui sotto, poi eseguirlo passo passo con F8.
Il problema mi si e' presentato in una routine
parecchio piu' complessa, ma l'ho semplificata
al massimo per renderla chiara e semplice da
verificare. Ovvio quindi che i codice qui sotto
non serve a nulla.
A prima vista si nota che la routine recursiva
viene chiamata con parametro = 0.
Incrementa il puntatore all'array (=1) e ci mette
0 dentro.  Poi chiama se stessa passandosi
il parametro 1 (il puntatore su cui ha appena
scritto).  Fin qui tutto bene, ma ora dovrebbe
inserire nella posizione 2 il valore 1.  Cosi' e'
in teoria, ma nel momento in cui viene eseguita
l'istruzione p=p+1 anche il valore di Parm
cambia, col risultato che mi mette 2 nell'elemento
2 dell'array. Perche' accade ?
Sono pazzo io o VB6 ha fatto una spece di Union
(come il C) fra Parm e il puntatore p ?
a me sembra un baco bell'e buono.
Per verificarlo con chiarezza, quando con F8
l'istruzione che sta per essere eseguita e'
"p=p+1" controllate quanto vale Parm.  Poi
con F8 eseguite p=p+1 e ricontrollate Parm
che sara' cambiato contemporaneamente a p.
Che ne dite ?  Accade anche con altre versioni
di VB ?
Option Explicit
Dim x(1 To 10) As Long
Dim p As Long
'---------------------------------------------------------------
Private Sub Form_Load()
  p = 0
  Recursivo 0
End Sub
'----------------------------------------------------------------
Private Sub Recursivo(Parm As Long)
p = p + 1                     ' punta all'elemento dell'array
If p < 11 Then
  x(p) = Parm
  Recursivo p
End If
End Sub
'------------------------------------------------------------------
Ciao
Beppe

 Beppe
Bye by Massimo 8-]

 End Sub
Esegui cosi`:
Option Explicit
Dim x(1 To 10)  As Long
Dim p           As Long
Private Sub Form_Load( _
)
  Dim i As Long
  p = 0
  Recursivo 0
  Debug.Print "p="; p
  For i = LBound(x) To UBound(x)
    Debug.Print x(i);
  Next
End Sub
Private Sub Recursivo( _
  ByRef Parm As Long _
)
  Debug.Print "Ingresso: ", "Parm="; Parm, "p="; p
  p = p + 1
  If p < 11 Then
    x(p) = Parm
    Debug.Print "Uscita: ", "Parm="; Parm, "p="; p
    Recursivo p
  End If
End Sub
e ottieni:
Ingresso:     Parm= 0       p= 0
Uscita:       Parm= 0       p= 1
Ingresso:     Parm= 1       p= 1
Uscita:       Parm= 2       p= 2
Ingresso:     Parm= 2       p= 2
Uscita:       Parm= 3       p= 3
Ingresso:     Parm= 3       p= 3
Uscita:       Parm= 4       p= 4
Ingresso:     Parm= 4       p= 4
Uscita:       Parm= 5       p= 5
Ingresso:     Parm= 5       p= 5
Uscita:       Parm= 6       p= 6
Ingresso:     Parm= 6       p= 6
Uscita:       Parm= 7       p= 7
Ingresso:     Parm= 7       p= 7
Uscita:       Parm= 8       p= 8
Ingresso:     Parm= 8       p= 8
Uscita:       Parm= 9       p= 9
Ingresso:     Parm= 9       p= 9
Uscita:       Parm= 10      p= 10
Ingresso:     Parm= 10      p= 10
p= 11
 0  2  3  4  5  6  7  8  9  10
Poi cosi`:
Option Explicit
Dim x(1 To 10)  As Long
Dim p           As Long
Private Sub Form_Load( _
)
  Dim i As Long
  p = 0
  Recursivo 0
  Debug.Print "p="; p
  For i = LBound(x) To UBound(x)
    Debug.Print x(i);
  Next
End Sub
Private Sub Recursivo( _
  ByVal Parm As Long _
)
  Debug.Print "Ingresso: ", "Parm="; Parm, "p="; p
  p = p + 1
  If p < 11 Then
    x(p) = Parm
    Debug.Print "Uscita: ", "Parm="; Parm, "p="; p
    Recursivo p
  End If
End Sub
e ottieni:
Ingresso:     Parm= 0       p= 0
Uscita:       Parm= 0       p= 1
Ingresso:     Parm= 1       p= 1
Uscita:       Parm= 1       p= 2
Ingresso:     Parm= 2       p= 2
Uscita:       Parm= 2       p= 3
Ingresso:     Parm= 3       p= 3
Uscita:       Parm= 3       p= 4
Ingresso:     Parm= 4       p= 4
Uscita:       Parm= 4       p= 5
Ingresso:     Parm= 5       p= 5
Uscita:       Parm= 5       p= 6
Ingresso:     Parm= 6       p= 6
Uscita:       Parm= 6       p= 7
Ingresso:     Parm= 7       p= 7
Uscita:       Parm= 7       p= 8
Ingresso:     Parm= 8       p= 8
Uscita:       Parm= 8       p= 9
Ingresso:     Parm= 9       p= 9
Uscita:       Parm= 9       p= 10
Ingresso:     Parm= 10      p= 10
p= 11
 0  1  2  3  4  5  6  7  8  9
A te quale dei due codici interessa?
--
Ciao.
Maurizio
--------
?SPQR(C)
 X
--------


Grazie Maurizio, per la tua risposta e soprattutto
per la cura e dedizione che ci hai messo.
Ma lo scopo del mio post non era quello di trovare
una soluzione.  Appena visto il problema, l'ho risolto
in altro modo (facendo una copia di Parm subito
all'ingresso nella routine).
Volevo solo far presente quello che accadeva in
questo caso, in modo che anche voi poteste
provarlo, e, oltre che evitare di impazzire in casi
simili (come e' successo a me) possiate aiutarmi a
capire perche' succede, se si tratta di un baco o
cosa altro.  Mi e' capitato, come dicevo, in una routine
molto piu' complessa, e capire dov'era l'errore non
e' stato facile.  (Se guardi l'ora del mio primo post...)
Quindi la domanda era: *perche'* succede ?
Ovvero, quale puo' essere la regola generale da
seguire per evitare di incocciare in problemi analoghi?
Cmq grazie a tutti per le vostre risposte !
Beppe

Quindi la domanda era: *perche'* succede ?
Beh... la domanda adesso č: perche' succede *cosa*?
Hai spiegato cosa succede, ma non cosa non ti quadra e perche'.
Ciao


Perche' quendo esegue p=p+1 contemporaneamente
varia anche la var Parm ?
Ho fatto altre prove e credo proprio che il problema
sia dovuto al passaggio byref della variabile, cosa
che in effetti fa si che entrambe p e Parm puntino
alla stessa memoria.
In conclusione da questo si deduce che bisogna fare
attenzione, in certi casi alcuni errori "misteriosi"
potrebbero essere causati da un passaggio di
parametri non adatto.
Ciao
Beppe

 parametri non adatto.
Concordo pienamente.
VB ha il vizietto di passare tutto ByRef se non diversamente specificato e
anche a me e' capitato di incocciare in un mistero analogo. Da allora, se
non ho un motivo valido per il passaggio del parametro per riferimento,
premetto sempre ByVal.
(passaggio di oggetti a parte).
Ciao
Aldo

 Cmq grazie a tutti per le vostre risposte !
--


 Scrivere SEMPRE ByVal o ByRef, cosi` si e` costretti a pensare.
Basta pensare e basta. E mettere byref o byval solo
dove serve (e se serve).
Senza rancore.
Anzi, senza
Ciao
Beppe

 dove serve (e se serve).
Imho serve sempre.
basti dire che dalla prossima versione di vb il passaggio dei parametri
avverra' ByVal di default (al contrario di adesso).
Ciao
Aldo

 avverra' ByVal di default (al contrario di adesso).
Allora serve si.  Se invertono il default c'e'
il rischio che cio' che funzionava su VB6
con la versione successiva non funge piu'.
Se quel che dici e' vero, allora conviene sul
serio specificarlo dovunque.
Il fatto e' che specificare sempre nelle routines
con molti parametri e' una rottura mica da poco.
Chissa' se saranno furbi da fare una Option
(tipo option base o option explicit) per poter
predefinire il default da utilizzare.
Ciao
Beppe

 Anzi, senza
Vedo che sei molto aperto al dialogo. La prossima volta invece di postare un
quesito e basta aggiungi anche un questionario a risposta chiusa cosi` ti
garantisci che ti venga risposto solo quello che hai previsto.
--


> Ci vuole anche disciplina, applicazione e umilta`.
Certo, basta leggere tutte le risposte che
dai tu sul NG per imparare la disciplina e
soprattutto l'umilta' (!!)
Siccome non ho ne' il tempo ne' la voglia di
andare avanti con una cretinata che rischia
di finire in flame, e non ho voglia di perdere
il mio tempo con trolls e affini, ti saluto e ti
ringrazio per l'insegnamento che Sua Maesta'
mi ha fornito:
Sui NG frequentati da sedicenti padreterni
e' meglio non postare.   O si rischia di essere
trattati da incompetenti o da poveri deficienti.
Saluti e baci
Buon anno
Beppe
P.S. Non hai risposto alla mia domanda:
chi ca@@o sei ? un ragazzino smanettone?
Il responsabile sw della M$ ?

 Il responsabile sw della M$ ?
Non vedo perche' dovrei pubblicamente rispondere ad una domanda personale.
Non ti sembra indelicato? E che importanza puo` avere? Forse che le stesse
parole, se scritte da uno o dall'altro dei due personaggi che citi, cambiano
di significato?
--
Ciao.



Okappa, ora sentite questa:
Public Function GetCommand(ByRef sPath As String) As Long
    Static pPath As String
    If pPath = "" Then
        sPath = Trim(Command$)
        pPath = sPath
    Else
        sPath = pPath
    End If
End Function
Orbene, in alcuni casi sPath viene ricevuta ByVal.
Sono CERTO di passarla byref, ho tracciato tutto il procedimento in debug,
arrivo a una linea che passa una variabile byref, guardo lo strptr, entro
nella procedura, guardo lo strptr e, sorpresa sorpresa, sono diversi!!!
Ma la cosa assurda e' che la prima chiamata (fatta da un'altra sub) avviene
ByRef (infatti mi ritorna correttamente la linea di comando, che uso per
localizzare i file da trattare), la seconda NO...
Vi pare logico???
Possibile che una chiamata intermodulo dia problemi (sono moduli.bas, non
.cls)?
Ciaotutti,
Alessandro


Lui e' Maurizio Borrelli.
C'e' scritto, basta leggere :-)
Prima di rispondere tieni conto che potrebbe esserci scritto qualcosa tra le
righe...
Ciaotutti,
Alessandro

 localizzare i file da trattare), la seconda NO...
Qui non ho capito bene. Ho provato a chiamare la GetCommand() in un ciclo
for da 0 a 10, ma il contenuto della pPath non e' mai cambiato dopo la prima
assegnazione.
Ciao
Aldo


Infatti, pPath non cambia, il  problema e' che quello che dovrebbe essere il
mio retval non returna... Nel senso che rimane assolutamente uguale a quello
che avevo passato.
[FLASH!!!]
[FLASH!!!]
[FLASH!!!]
Oddio ho appena avuto un lampo di genio aspettate un secondo..
[SNIP temporale]
Si, leggete questa:
sPath="pippo"
GetCommand (sPath)
Qui probabilmente Maurizio Borrelli (da un recente post del quale su ASP ho
capito l'errore) e' in grado di dire cosa sia sbagliato in questa chiamata e
perche', scrivendola in questo modo, la variabile venga EFFETTIVAMENTE
passata byval.
C'e' da uccidere qualcuno a Red(im)mond per questo, cmq...
Ciaotutti,
Alessandro

 ByRef e dal fatto che VB passa implicitamente tutto ByRef.
Non so se nel frattempo hai trovato qualcosa anche tu, ma finalmente ho
recuperato quello che ricordavo un po' vagamente.
Da questo url
(
http://www.vb2themax.com/HtmlDoc.asp?Table=Articles&ID=30&Page=2
) ti
riporto un piccolo estratto:
///// inizio estratto
While you can do a lot of neat tricks using StrPtr, string processing comes
into its own with VarPtr, another undocumented VB function (see Figure 1).
The VarPtr function usually returns the address of a variable, but when used
with string variables it returns the address of the string's descriptor (a
pointer to the first character of the string). I'm using the "descriptor"
term somewhat arbitrarily, because descriptors are more often associated
with structures. A stickler for language might call them BSTRs, but
"descriptors" is more familiar to VB programmers (especially to old salts
with QuickBasic experience).
///// fine estratto
E qui l' affare s' ingrossa :)
Scherzi a parte, si comincia a capire qualcosa. Se non altro viene
specificata la differenza fra VarPtr e StrPtr.
Ma andiamo avanti:
//////// inizio estratto
When VB compiles your program, it hard-codes references to strings as
references to descriptors. In a given procedure the x$ variable always
corresponds to a given descriptor, whose contents, however, may vary. That's
because VB may reallocate the memory area holding the characters when it
assigns a new value to the string. And since the value the StrPtr(x$)
function returns may change during execution, don't cache it in a variable
for later reuse-unless no string operations will be executed.
///// fine estratto
Quindi noi lavoriamo in realta' con i descrittori delle stringhe, ma le
allocazioni di memoria sono un affare esclusivo di vb.
Ci vorrebbero dei begli array di char :) e relativo puntatore per riprendere
il controllo della situazione.
Ma diventeremmo di colpo OT.
Ciao
Aldo


A parte il fatto che VarPtr passasse il ** il resto sostanzialmente lo
sapevo... Informalmente ma cmq era chiaro che VB giocava da solo con le
stringhe...
Il problema pero' non era quello: quando passi una stringa ByRef il suo
valore non cambia da fuori a dentro la funzione, il problema e' che quella
stringa veniva passata ByVal,  perche' io facevo la chiamata scrivendo
funzione (stringa)
anziche'
call funzione (stringa)
o
var = funzione(stringa)
E quindi lui faceva il CAST della variabile da string a string, passandola
in questo modo byval...
Ciaotutti,
Alessandro

in questo modo byval...
E già, hehe, questo è un rimasuglio del vecchio Basic per DOS.
Ciao