12

Context 1

var text:String;

text:='hello';

myFunc(text);

Context2

function myFunc(mytext:String);
var textcopy:String;
begin

    textcopy:=mytext;

end;

myFunc on the Context2 was called from the Context1, the local variable mytext is pointing to a memory outside the Context2? or the mytext have have their own memory space inside the scope, and filled/copied with the same content of the text? I'm probably missing something really basic, because I'm getting a access violation error.

There's any way to specify explicitly if a function should receive parameters by reference or by value, copying then like in C? I'm not sure about how I'm doing it.

Vitim.us
  • 20,746
  • 15
  • 92
  • 109
  • 1
    Normally, this should not result in an AV! I suspect these codes are run from separate DLL's? Use `ShareMem` then. – NGLN Mar 03 '12 at 10:59
  • and 'some error of access violation' is? – RBA Mar 03 '12 at 15:14
  • @NGLN yes I'm using a dll written in C++ so I wrote a pas header file. And I'm getting AV in situations that I can't figure out why, probably this passing-by-reference and not copying is causing AV when I work with pointers. – Vitim.us Mar 03 '12 at 22:35

3 Answers3

27

Memory management for Delphi strings is a little unusual. After you call myFunc(text), and assign textcopy := mytext, all three variables (text, mytext and textcopy) will be pointing to the same address, that of the original string.

But as soon as you use one of these variables to make changes to the string, Delphi clones the string behind the scenes, and your changes are applied to the copy. The other two variables still point to the original, so they remain unchanged. So any changes made in Context 2 will not be seen in Context 1 - this "copy-on-write" mechanic effectively gives you pass-by-value semantics. All of these strings are reference-counted, and will be freed automatically once all references go out of scope.

However, there is an exception. If you access the string using pointers, rather than string operations, you'll bypass the copying step and your changes will affect the original. You'll also bypass the reference counting logic, and potentially end up with a pointer to a deallocated block of memory. This may be the reason behind your access violation, but I couldn't say without more details / more code.

If you want reference passing, declare your function as myFunc(var mytext: String). If you want to force Delphi to copy the string, instead of waiting until it's modified, you can use System.UniqueString.

Nick Barnes
  • 19,816
  • 3
  • 51
  • 63
  • 1
    That's true but it's not really helpful to a complete beginner. The point about all the copy on write magic is that it makes a reference type behave like a value type. I fear that this answer, correct though it is, will merely further confuse an already very confused novice. – David Heffernan Mar 03 '12 at 08:49
  • 3
    @David: I don't know, sounded to me like like he has a decent enough grasp of call semantics and memory allocation in general, just not how they work in Delphi specifically. And while I'd certainly agree that it's usually better to gloss over the details and let Delphi work its magic, I figured that an access violation is going to demand a lower-level explanation. – Nick Barnes Mar 03 '12 at 12:05
  • Edit much improved answer. AV is unrelated. See other question. – David Heffernan Mar 03 '12 at 12:50
  • _you'll need to do it yourself with System.Copy()_, or use [`UniqueString`](http://docwiki.embarcadero.com/VCL/en/System.UniqueString). – NGLN Mar 03 '12 at 12:52
  • @NickBarnes I received a notification on this, and I noticed that I didn't wrote any feedback (idk why). But thanks anyway this was really helpful, I'm not any expert, but I do understand the concept of how memory, pointers, semaphores and other low level things work, I think everyone should read about. This copy-on-write really give the false though of passbyvalue, but I was doing low level pointer operations using a dll written in C to access a driver, and delphi was pointing to a deallocated memory giving me AV. The System.UniqueString did the job. – Vitim.us Feb 05 '13 at 00:50
  • I like it. I wonder if its possible to re-asssign a pointer on the fly?! – Steve F Nov 28 '15 at 15:53
14

In Delphi, string is a reference type that normally acts like a value type. It is allocated on the heap (not the stack like most value types) and features automatic reference counting and copy-on-write semantics.

To understand what this means, consider how normal value types, e.g. an Integer, behave when passed as a parameter to a procedure:

var
  gValue: Integer;

procedure PassByValue(aValue: Integer);
begin
  // Here @gValue <> @aValue
  aValue := aValue + 2;
  // Here @gValue <> @aValue
end;

procedure PassByRefrenceInOut(var aValue: Integer);
begin
  // Here @gValue = @aValue
  aValue := aValue + 2;
  // Here @gValue = @aValue
end;

procedure CallProcedures;
begin
  gValue := 0; 
  PassByValue(gValue);
  // gValue is still 0
  PassByReferenceInOut(gValue);
  // gValue is 2
end;

The var parameter in PassByReferenceInOut is equivalent to the C convention of passing a pointer to the argument.

The same semantics apply to string parameter passing, but there is a subtle difference in the internal representation of the values:

var
  gValue: string;

procedure PassByValue(aValue: string);
begin
  // Here PChar(gValue) = PChar(aValue) <<<<
  aValue := aValue + '2';
  // Here PChar(gValue) <> PChar(aValue)
end;

procedure PassByRefrenceInOut(var aValue: string);
begin
  // Here PChar(gValue) = PChar(aValue)
  aValue := aValue + '2';
  // Here PChar(gValue) = PChar(aValue)
end;

procedure CallProcedures;
begin
  gValue := ''; 
  PassByValue(gValue);
  // gValue is still ''
  PassByReferenceInOut(gValue);
  // gValue is '2'
end;

If you want to make sure a procedure operates on its own copy of the string value, use the UniqueString procedure, e.g.:

procedure PassByValue(aValue: string);
begin
  // Here PChar(gValue) = PChar(aValue)
  UniqueString(aValue);
  // Here PChar(gValue) <> PChar(aValue)
  aValue := aValue + '2';
  // Here PChar(gValue) <> PChar(aValue)
end;
Henrick Hellström
  • 2,556
  • 16
  • 18
9

In Delphi to pass by reference you explicitly add the var keyword:

procedure myFunc(var mytext:String);

This means that myFunc can modify the contents of the string and have the caller see the changes.

Mike W
  • 1,276
  • 8
  • 10