Page 1 of 2
Passing strings to procedures "By Ref"(theoretical
Posted: Tue May 22, 2007 11:48 pm
by iNX
Let's suppose i want to pass a string to a procedure which modifies the string contents.
If i pass a structure of type .string i can do everything with the string defined inside the structure.
For example:
Code: Select all
Procedure ModString(*sa.String)
*sa\s = "123456789"
EndProcedure
sp.String
sp\s="12" ; this is optional
ModString(@sp)
Debug sp\s ; returns "123456789"
So i can assign a greater number of characters and memory allocation will be handled automatically.
But if start with a simple string (sp$="12"), without defining a structure of type .string, is it correct to say that i can't achieve something similar to the example above, without handling manually memory allocation/deallocation? (or maybe i can't at all)
Obviously i'm not taking into consideration of returning the string from the Procedure (ModString().s) or using globals.
Code: Select all
Procedure ModString(???)
???
EndProcedure
sp$="12"
ModString(???)
Debug sp$
Posted: Wed May 23, 2007 12:40 am
by srod
You can certainly achieve the same results using the PB memory commands, but you will most probably have to take care of freeing the resulting memory block yourself. On the other hand, your first solution will enable Purebasic to handle the garbage collection automatically.
I know which method I would use!

Posted: Wed May 23, 2007 8:47 am
by iNX
Thank you srod, you gave me confirmation of what i was thinking.
Posted: Wed May 23, 2007 9:27 am
by Trond
Code: Select all
Procedure ModString(*sa.S)
*sa = "123456789"
EndProcedure
sp.s
sp = "12"
ModString(@sp)
Debug sp
But be careful, the *.s type is a bit flaky since PB 4.
Edit: By the way, there is no "by ref" in PB, everything is by value (by value of pointer).
Posted: Wed May 23, 2007 1:18 pm
by iNX
@Trond
that's interesting. It's the most obvious thing, but it works flawlessly!
I think i didn't find this solution because i got confused by many topics about pointers and strings, and even by the PB documentation itself.
For example, in the PB help, i read: "As a consequence the type of a pointer depends of the CPU address mode, (‘long’ on 32 bits CPU and ‘quad’ on 64 bits one for example), so a pointer is a variable of type pointer.
It results from this that assigning a type to a pointer (*Pointer.l , *Pointer.b …) makes no sense."
But from what you showed to me i would say that .s assigned to a pointer has much sense, because it allows the pointer to be dereferenced automatically. And it seems to me that this only happens with pointers of type .s, making possible to modify the contents of the string the pointer is referencing, with further memory allocation (if needed) automatically handled .
For example, if i have a *p.l as a procedure argument, and in the procedure i use *p=10 i think that i'm not writing 10 to the variable whose address i'm passing through p, but i think that i'll write 10 to the pointer itself, screwing things up, since 10 is obviously not a valid memory address.
Posted: Wed May 23, 2007 1:44 pm
by Trond
Yes, it only makes sense with *.s, no other standard types.
Posted: Wed May 23, 2007 2:19 pm
by iNX
Some more things:
Code: Select all
Procedure ModString(*sa.S)
sb.s
sb="abcd"
*sa = "123456789"
*sa = @sb ; "can't write a numerical value into a string variable" error
EndProcedure
sp.s
sp = "12"
ModString(@sp) ; here i'm assigning an address to *sa.s
; but assigning directly an address to *sa ( *sa=@anystring) is not allowed
Debug sp
So it's possible to assign a -pointer to a string- (@anystring) to a *stringpointer.s through a function argument, but non directly (*stringpointer=@anystring).
Posted: Wed May 23, 2007 2:29 pm
by srod
I would say that in the declaration:
because .s is not regarded as a structure in Purebasic, then *ptr is nor regarded, in this case, as a pointer (in the PB sense), but is regarded as a string variable instead.
The reason your code 'works' in a procedure declaration is undoubtedly because string parameters are handled by placing addresses on the stack anyhow.
Indeed, Fred might well consider this to be a bug - I don't know. It's interesting though.
Posted: Wed May 23, 2007 3:21 pm
by Trond
iNX, yes, there are some inconsistencies that shouldn't be there. Fred said he was looking (presumably for the cause).
srod, it should work.
Posted: Wed May 23, 2007 8:52 pm
by Psychophanta
Posted: Wed May 23, 2007 11:58 pm
by iNX
@ Psychophanta
Thank you for the links. I read them all, and i'll start quoting from the last one you posted, the one you started.
Code:
*var2.b=AllocateMemory(100); *var2 is a pointer (i.e. in a 32 bit architecture *var2 is a long -32 bit- ), which is suposed to point to a byte variable.
*var3.s=AllocateMemory(100); *var3 is a pointer (i.e. in a 32 bit architecture *var3 is a long -32 bit- ), which is suposed to point to a string variable.
As you can see I can not define a pointer to a string and assign it a pointer value Surprised
Maybe you had already discovered it by yourself, however as you can read from Trond's posts above and from my answers to it, there's actually one way to assign a pointer to a string to a *var3.s object: utilizing *var3.s as a parameter of a procedure.
I was thinking that passing strings this way was safe for modifying these strings within procedures, since PB would have handled all needed memory allocation in case that i had written to the string a number of characters greater than the size it had before the procedure call.
But i've just discovered this:
Code: Select all
Procedure ModString(*sa.s)
*sa="21" ; this is ok
;*sa="2434567892434567892434" ; this screws things up
EndProcedure
sp.s
st.s
sp="12"
st="16"
ModString(@sp)
Debug sp
At this point, the use of *stringpointer="something" imho makes even less sense, and maybe should be prohibited, so that *stringpointers.s will be numeric and people will have to use Pokes to use them for writing.
Maybe this way none will think what i was thinking (wrongly) about automatic memory allocation.
Posted: Thu May 24, 2007 12:18 am
by srod
To my mind, PB has it right in that pointers, in terms of de-referencing operations at least, are intended really for structures. It keeps things very 'clean' and simple.
Incidentally, the following is a kind of workaround in that you don't need to define a structure of type STRING in order to call the procedure and avoids Peek's and Poke's!
Code: Select all
Procedure ModString(a)
*x.string=@a
*x\s="2434567892434567892434" ; this screws things up no longer!
EndProcedure
x.s
x="ab"
ModString(@x)
Debug x
Posted: Thu May 24, 2007 9:03 am
by iNX
srod wrote:To my mind, PB has it right in that pointers, in terms of de-referencing operations at least, are intended really for structures. It keeps things very 'clean' and simple.
My opinion is that pointers are really useful and indispensable. I'm only wondering about the need for a *stringpointer.s="pppppp" that acts differently from all other pointers, acts differently if used in a procedure parameter or in a standalone assignment, and may also lead to errors because it doesn't handle memory allocation (or something like that).
Incidentally, the following is a kind of workaround in that you don't need to define a structure of type STRING in order to call the procedure and avoids Peek's and Poke's!
Code: Select all
Procedure ModString(a)
*x.string=@a
*x\s="2434567892434567892434" ; this screws things up no longer!
EndProcedure
x.s
x="ab"
ModString(@x)
Debug x
Unfortunately the above procedure doesn't work too.
You can see it if you add another variable. Try this way:
Code: Select all
Procedure ModString(a)
*x.string=@a
*x\s="2434567892434567892434767767677676" ; this screws things up
EndProcedure
x.s
y.s
x="ab"
y="cd"
ModString(@x)
Debug x
I think that this is happening because what you're doing here is a sort of manually recreating a structure containing a s.s, using a pointer to pointer. But probably the compiler doesn't see it as a real structure, so it doesn't handle memory properly.
In the end, it would seem that the only way for passing a string to a procedure and being able to modify it freely from inside the procedure, was utilizing a structure of type .string instead of a simple string, and passing a pointer to that structure like in the first code snippet of this thread. ( not considering inline assembly and/or manual memory allocation/garbage collection)
edit:
Here we can see how memory is handled properly, with a reallocation of the x\s buffer.
Code: Select all
Procedure ModString(*a.string)
*a\s="2434567892434567892434767767677676"
EndProcedure
x.string
y.string
x\s="ab"
y\s="cd"
Debug @x\s
ModString(@x)
Debug x\s
Debug @x\s ; here we can see that the buffer of x\s has been reallocated since its address has changed.
Posted: Thu May 24, 2007 10:36 am
by srod
Interesting. Yes I agree that your last code is fine and is generally the way I proceed anyhow.
I've had a look at the asm generated by my code and am not sure where the problem is. I can only guess that memory is being freed before time somewhere along the line! Still, the whole business of using pointers in this way doesn't sit right with me anyhow!

Posted: Thu May 24, 2007 12:15 pm
by iNX
Ok, i investigated further, looking at the asm sources and i should have found out where the problem lies.
A bigger problem will be being able to explain it
Code: Select all
Procedure ModString(a)
*b.string=@a
*b\s="2434567892434567892434767767677676" ; At this point, the internal function that handles string assignments (SYS_FastAllocateStringFree) receives the real pointer to the buffer of string x, but we could say that this pointer is in a bad location (ie: it is on the stack). So what happens next? SYS_Fast.. frees the buffer of string x because it's too small, allocates a new buffer, copies the new string value into the new buffer and puts the address of this new buffer on the stack. So the new buffer it's not "associated" with the real string x (ie: put in v_x), but it's "associated" with the fake b.string structure (that in this case is just a pointer on the stack) and gets lost.
If y is not declared, then the new allocated buffer will remain at the same memory position and the problem won't show up.
x.s
y.s
x="ab"
y="cd"
ModString(@x)
Debug x
Let me know if If i have been unclear and i'll try to clarify
