Confusion over passing by reference vs passing by value
#1
Let's say that I have the following code:

Code: (Select All)
Option _Explicit

Dim x As Integer
Dim a As String

Print "Passing a numerical variable by reference:"
x = 1
Test1 x
Print "Value of x after subroutine:"; x

Print

Print "Passing a numerical variable by value:"
x = 1
Test1 (x)
Print "Value of x after subroutine:"; x

Print

Print "Passing a string variable by reference:"
a$ = "String1"
Test2 a$
Print "Value of a$ after subroutine:"; a$

Print

Print "Passing a string variable by value:"
a$ = "String1"
Test2 (a$)
Print "Value of a$ after subroutine:"; a$

End


Sub Test1 (y As Integer)
    y = y + 1
End Sub

Sub Test2 (b As String)
    b$ = b$ + " and string 2"
End Sub


This code has 4 main sections:

Section 1: We set as variable "x" to 1 and pass it to a subroutine by reference to the variable "y". The subroutine adds one to the value of y. As expected, when we come out of the subroutine we find that the original variable "x" is now equal to 2.

Section 2: We set as variable "x" to 1 and pass it to a subroutine by value to the variable "y". The subroutine adds one to the value of y. As expected, when we come out of the subroutine we find that the original variable "x" is unchanged because we passed the variable by value rather than by reference.

But what about strings?

In section 3 and 4, I'm doing the same thing but with a string. I pass it to a subroutine both by reference and by value but in both cases the original string variable is changed.

Is this expected behavior? Is it possible to pass a string variable without affecting the original?

I know that I could work around it by assigning the variable to another variable within the subroutine like this:

Code: (Select All)
Sub Test2 (b As String)
dim c as string
c$=b$   
c$ = c$ + " and string 2"
End Sub


This way, I'm not modifying the original string. I just wanted to make sure I'm not missing something obvious.
Reply
#2
To be honest it looks like a bug to me. I checked the behavior in QB45 to be sure, and using () to pass a string by value works as expected. Additionally it seems extra bugged because doing
(a$ + "")
also does not properly pass by value, and neither does
a$ + ""
(both of which pass by value in QB45). Functionally though the problem is just that it is not being properly copied before calling the function, so they're all basically the same issue.

Edit: I created a GitHub issue for this: https://github.com/QB64-Phoenix-Edition/...issues/116
Reply
#3
This is expected as strings are *always* passed by offsets. The solution is as you have found -- assign to a temp variable and don't modify the original. Wink
Reply
#4
(06-17-2022, 03:07 AM)SMcNeill Wrote: This is expected as strings are *always* passed by offsets.  The solution is as you have found -- assign to a temp variable and don't modify the original.  Wink

Ah, very good. I wasn't aware that there was a difference between how the strings and numerical variables were handled in this regard.

I learn new things every day. The trick is in trying to remember all of it :-)
Reply
#5
Quote:Ah, very good. I wasn't aware that there was a difference between how the strings and numerical variables were handled in this regard.

FWIW I think that Steve and I have different opinions on this Tongue They're intended to all work the same, as they did in QB45 (there's a reason the Wiki doesn't say anything about strings and stuff working differently Big Grin). But that said it seems that after some investigation QB64 has significant problems regarding pass by value behavior for anything not "simple" like integers. I think it should be a bug considered for fixing, but either way it's probably best to avoid it for now.
Reply
#6
(06-17-2022, 02:21 PM)DSMan195276 Wrote:
Quote:Ah, very good. I wasn't aware that there was a difference between how the strings and numerical variables were handled in this regard.

FWIW I think that Steve and I have different opinions on this Tongue They're intended to all work the same, as they did in QB45 (there's a reason the Wiki doesn't say anything about strings and stuff working differently Big Grin). But that said it seems that after some investigation QB64 has significant problems regarding pass by value behavior for anything not "simple" like integers. I think it should be a bug considered for fixing, but either way it's probably best to avoid it for now.

I don't think we're of different opinions -- I agree 100% with the statement that the way we handle it is currently bugged.  

My opinion on the issue however is: I'm not smart enough to sort out how the BLEEP to fix the problem, so it's best -- for now -- for folks to learn to avoid the possible pitfalls that can come from using the glitchy "pass via parentheses" method.  

Personally, I'd love to see the issue fixed.  I just couldn't sort out how to fix it myself, so until it does get fixed, I figure it's best to just warn folks to be very careful with such code and to use temp or STATIC variables when possible to avoid the issue completely.  Wink
Reply
#7
As Steve said, using temp variables is the tried and true simple solution.
For more complex data structures like arrays of user-defined types, you could make copies of just the values you need to use, or if you want to get fancy, you could get into memcopy (spriggsy knows about that stuff), though I am more comfortable with keeping things simple where possible.
Reply
#8
Why is the transfer of the sentence "By value" unsuccessful?

Row 48 -Satztest3 (satz3())- generates a compiler error.

Code: (Select All)
'Unterschiede bei Uebergabe einer Variablen, Zahl und String
'Call by reference (automatisch) gibt die Adresse der Variablen
'Variable kann veraendert werden.

'Call by value (Klammerung der Variablen) standardmaessig,
'wird eine Kopie uebergeben; keine Aenderung

Option _Explicit

Dim x As Integer
Dim satz As String
Dim satz2(2) As String, satz3(2) As String

Print
Print "x = 1 - Satz ist: Hallo, Fans + von QB64": Print

Print "Variable by reference: x + 1 = 2"
x = 1
Zahlentest x
Print "Wert von x nach Verlassen der Subroutine:"; x

Print
Print "Variable by value: Keine Veraenderung"
x = 1
Zahlentest (x)
Print "Wert von x nach Verlassen der Subroutine:"; x

Print
Print "String Variable by reference:"
satz = "Hallo, Fans"
Satztest satz 'Hier sollte die Adresse uebergeben werden.
Print "Wert des Strings satz nach Verlassen der Subroutine: "; satz
'Der auszugebende Satz muesste hier lauten: von QB64!

Print
Print "String variable by value:"
satz = "Hallo, Fans"
Satztest (satz) 'Hier wird eine Kopie erzeugt, und "von QB64" wird angehaengt
Print "Wert des Strings satz nach Verlassen der Subroutine: "; satz

Print
satz2() = "Sag niemals nie!"
Satztest2 satz2()
Print "Wert des Feldes von satz2 nach der Subroutine: ", satz2()

Print
satz3() = "Sag niemals nie!"
'Satztest3 (satz3()) '!! Damit hat der Compiler Probleme
Print "Wert des Feldes von satz3 nach der Subroutine: ", satz3()

End


Sub Zahlentest (y As Integer)
  y = y + 1
End Sub

Sub Satztest (b As String)
  b = b + " von QB64!"
End Sub

Sub Satztest2 (feld() As String)
  feld() = "Hallo, Donald Duck!"
End Sub

Sub Satztest3 (feld() As String)
  feld() = feld() + "Donald Duck"
End Sub


Quote:internal\c\c_compiler\bin\c++.exe  -w -DGLEW_STATIC -DFREEGLUT_STATIC -DDEPENDENCY_NO_SOCKETS -DDEPENDENCY_NO_PRINTER -DDEPENDENCY_NO_ICON -DDEPENDENCY_NO_SCREENIMAGE internal\c/qbx.cpp -c -o internal\c/qbx.o
In file included from internal\c/qbx.cpp:2514:
internal\c/..\\temp\\main.txt: In function 'void QBMAIN(void*)':
internal\c/..\\temp\\main.txt:235:16: error: cannot convert 'qbs*' to 'long long int*'
SUB_SATZTEST3(((qbs*)(((uint64*)(__ARRAY_STRING_SATZ3[0]))[0])));
              ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from internal\c/qbx.cpp:1264:
internal\c/..\\temp\\regsf.txt:4:29: note:  initializing argument 1 of 'void SUB_SATZTEST3(long long int*)'
void SUB_SATZTEST3(ptrszint*_SUB_SATZTEST3_ARRAY_STRING_FELD);
mingw32-make: *** [Makefile:337: internal\c/qbx.o] Error 1
Reply
#9
Unfortunately a lot of cases involving pass-by-value don't work correctly. The specific one you ran into I made a GitHub issue about a couple days ago:

https://github.com/QB64-Phoenix-Edition/...issues/118
Reply
#10
(06-19-2022, 04:18 PM)DSMan195276 Wrote: Unfortunately a lot of cases involving pass-by-value don't work correctly. The specific one you ran into I made a GitHub issue about a couple days ago:

https://github.com/QB64-Phoenix-Edition/...issues/118

Quote:How the copy happens depends on what kind of array it is - for most arrays the backing buffer can just be copied directly, for variable length strings the qbs objects need to be copied individually.

Also not as one sentence.

Code: (Select All)
Print
satz3() = "SagNiemalsNie!"
Satztest3 (satz3()) '!! Damit hat der Compiler Probleme
Print "Wert des Feldes von satz3 nach der Subroutine: ", satz3()

End


Sub Zahlentest (y As Integer)
  y = y + 1
End Sub

Sub Satztest (b As String)
  b = b + " von QB64!"
End Sub

Sub Satztest2 (feld() As String)
  feld() = "Hallo, Donald Duck!"
End Sub

Sub Satztest3 (feld() As String)
  feld() = feld() + "DonaldDuck"
End Sub
Reply




Users browsing this thread: 8 Guest(s)