I am trying to understand how are pointers to strings working. I have a code (not exactly original), which was written by somebody, and the person is not around here anymore, so I need to understand the idea of such usage.
var
STR: string;
pStr: ^string;
begin
STR := 'Hello world';
New(pStr);
pStr^ := STR;
PostMessage(Handle, WM_USER+1, wParam(pStr), 0);
end;
Now I know for sure, that a message handler gets the message and the pointer contains the string, which can be worked with, but what happens 'under the hood' of those operations ?
I tried to make a small project. I thought, that assigning string to what a str pointer is pointing to would actually increase refcount of the original string and not make any copies of a string, but refcount remained 1 and it seems it did copy the contents.
So get the question, what happened? Calling New on a pointer allocates an empty string, right?
After assignment I tried to look at refcount/length of a string the pointer pointed to like this PChar(#pStr^[1])[-8] but it returned nonsense (14), and the length byte was also wrong.
Additionally the questioin is, is it safe using pointers in such a way to pass on the string through windows messaging?
New(pStr) allocates a string on the heap and returns a pointer to it. Because string is a managed type, the string is default initialized, to the empty string. Since a string is implemented as a pointer, what you fundamentally have is a pointer to a pointer.
You code is perfectly fine, so long as you only post the message to your own process. Since the payload of the message is a pointer, it only means something in the context of the virtual address space of your process. If you wanted to send to a different process you'd need an IPC mechanism.
Clearly in the code that pulls the message off the queue you need to dispose of the string. Something like this:
var
p: ^string;
str: string;
....
p := Pointer(wParam);
str := p^;
Dispose(p);
Your code to query the reference count and the length is just wrong. Here's how to do it correctly:
{$APPTYPE CONSOLE}
var
pStr: ^string;
p: PInteger;
begin
New(pStr);
pStr^ := 'Hello world';
p := PInteger(pStr^);
dec(p);
Writeln(p^); // length
dec(p);
Writeln(p^); // ref count
Readln;
end.
Output:
11
1
Related
I am using Delphi TThread to run multiple TCP connections to external devices. Incoming data is dissected and stored. Everything works OK but on reviewing my code I am having doubts whether it is thread safe or if I am just being lucky so far...
In the TThread.Execute method, I am calling a helper function which exists in a different unit, not being a member of any TThread or other class. The function accesses four bytes of data in a buffer, reverses the byte order and places the result in a variable of type Single, (the external devices are Big Endian).
type
TByteBuffer = array [0 .. 255] of Byte;
function ConvBufferToSingle(Buffer: TByteBuffer; J: Byte): Single;
type
TSingleByteArray = array [0 .. 3] of Byte;
var
X: Single;
begin
TSingleByteArray(X)[3] := Buffer[J];
TSingleByteArray(X)[2] := Buffer[J + 1];
TSingleByteArray(X)[1] := Buffer[J + 2];
TSingleByteArray(X)[0] := Buffer[J + 3];
Result := X;
end;
Although this seems to work, I can't understand what happens if one (or more) threads enter this function before another thread has exited. I have read articles on Thread Safe vs. Re-Entrant code, but I am still not sure how to verify that the code is thread safe.
When the function is called, I believe that the parameters are pushed onto the stack. This would happen for each TThread entering the function, but what happens in the code addressing those variables confuses me.
For peace of mind, is it better practise to turn the function into a method of the thread class?
I've got this small code snippet to test 2 ways of converting byte slice to string object, one function to allocate a new string object, another uses unsafe pointer arithmetic to construct string*, which doesn't allocate new memory:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func byteToString(b []byte) string {
return string(b)
}
func byteToStringNoAlloc(b []byte) string {
if len(b) == 0 {
return ""
}
sh := reflect.StringHeader{uintptr(unsafe.Pointer(&b[0])), len(b)}
return *(*string)(unsafe.Pointer(&sh))
}
func main() {
b := []byte("hello")
fmt.Printf("1st element of slice: %v\n", &b[0])
str := byteToString(b)
sh := (*reflect.StringHeader)(unsafe.Pointer(&str))
fmt.Printf("New alloc: %v\n", sh)
toStr := byteToStringNoAlloc(b)
shNoAlloc := (*reflect.StringHeader)(unsafe.Pointer(&toStr))
fmt.Printf("No alloc: %v\n", shNoAlloc) // why different from &b[0]
}
I run this program under go 1.13:
1st element of slice: 0xc000076068
New alloc: &{824634204304 5}
No alloc: &{824634204264 5}
I exptect that the "1st element of slice" should print out the same address like "No alloc", but acturally they're very different. Where did I get wrong?
First of all, type conversions are calling a internal functions, for this case it's slicebytetostring.
https://golang.org/src/runtime/string.go?h=slicebytetostring#L75
It does copy of slice's content into new allocated memory.
In the second case you're creating a new header of the slice and cast it into string header the new unofficial holder of slice's content.
The problem of this is that garbage collector doesn't handle such kind of cases and resulting string header will be marked as a single structure which has no relations with the actual slice which holds the actual content, so, your resulting string would be valid only while the actual content holders are alive (don't count this string header itself).
So once garbage collector sweep the actual content, your string will still point to the same address but already freed memory, and you'll get the panic error or undefined behavior if you touch it.
By the way, there's no need to use reflect package and its headers because direct cast already creates new header as a result:
*(*string)(unsafe.Pointer(&byte_slice))
My program has following code:
function FooBar(const s: string): string;
var
sa: AnsiString;
begin
// ..........................
sa := AnsiString(s);
sa := AnsiString(StringReplace(string(sa), '*', '=', [rfReplaceAll]));
sa := AnsiString(StringReplace(string(sa), ' ', '+', [rfReplaceAll]));
result := string(sa);
// ..........................
end;
I noticed that the program did crash "somewhere" and FastMM4 said that I had written to a freed object. As soon as I have commented out "const", the program did work.
I have read the Delphi documentation about const arguments, but I can't figure out why the const argument crashes the program. I would love to understand it.
UPDATE: The program does only crash in Delphi 6 and only if optimization is ON. If the optimization is OFF, the program will work normally. Might it be a Delphi bug?
There are a few peculiar gotchas when it comes to const string parameters.
Many years ago I helped a colleague resolve a similar peculiar problem (D3 iirc). The following simplified example doesn't look like your specific issue, but it may give you some ideas:
type
TMyClass
FString: string;
procedure AppendString(const S: string);
end;
procedure TMyClass.AppendString;
begin
FString := FString + S;
end;
Now if you have an instance of TMyClass and try to call AppendString(FString); to double up the string, you may get an access violation. (There are a few other factors that can affect if you do.) The reason is as follows:
The const prevents refcounting the string on the method call.
So FString may have refCount = 1 when its value is changed.
In which case Copy-on-Write doesn't apply and the string is reallocated. (Most likely at a different address.)
So when the method returns, S is referring to an invalid address, and triggers an AV.
For this case:
sa := s;
automatic reference counting (ARC) works. This is idiomatic way, compiler know how to work with these string - if sa will change, it creates new copy and so on.
For case of hard typecasting (though type is the same)
sa := AnsiString(s);
you tell compiler that you want to get only pointer to string, and you know how to work with this string reference. Compiler will not interfere and annoy you, but you are responsible for correct operations
P.S. I can not reproduce the problem with Delphi XE5 - both simple assigning and typecasting causes LStrLAsg (internal function) call with ARC. (Of course, compiler magic could be changed a bit)
We debugged a crash today that was a Delphi 5 compiler code-gen bug:
procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
begin
s := 'Hello, world! '+IntToStr(7);
DoSomething(s);
//String s now has a reference count of zero, and has already been freed
ShowMessage(s);
end;
procedure TForm1.DoSomething(const Title: string);
var
s: AnsiString;
begin
s := AnsiString(Title);
if Now = 7 then
OutputDebugString('This is a string that is irrelevant');
end;
The Delphi 5 compiler mistakenly tries to decrement the reference counts of both
Title
s
and causes the const string's reference count to go to zero. This causes it to be freed. So when DoSomething returns, you are using a freed string.
An access violation waiting to happen.
Or, in the case of customers: happening.
That becouse AnsiString is a pointer. So, you could'n operate it as a usual strings (which mean a array of chars). Crash is random, so you can gain the crash as result of stackoverflow or access violations at any time, not depend on optimization.
Than you function FooBar return the result, sa variable are already free from mamory.
Try to use the PChar or PAnsiChar and allocate the memory for this variables as your needs.
Since it doesn't make sense to convert to AnsiString and back here, I would rewrite this as
function FooBar(const s:string):string;
begin
Result:=
StringReplace(
StringReplace(
s
,'*','=',[rfReplaceAll])
,' ','+',[rfReplaceAll]);
end;
My app has a worker thread, and I use PostMessage to send a string to the main thread. For 1 message, the string is truncated when it gets to the message handler in the main thread.
The string is constructed in the worker thread from a string of raw data that is like this. It ends at the last '20'.
'01010000000030000102000008850008855343414E204544474520000000000000000000'
Decoded into the string I want to send it looks like this, which is correct:
'0100 0.50000 LSB0288.588.5SCAN EDGE '
The code that creates the 'SCAN EDGE ' portion and posts it is:
tmp and s_out are strings
x := 35;
for i := 1 to 10 do
begin
tmp := '$' + copy(s,x,2);
TryStrToInt(tmp,dec);
s_out := s_out + chr(dec);
x := x + 2;
end;
PostMessage(MainHandle,UM_CLONE, UM_756, Integer(PChar(s_out)));
The message handler in the main thread is:
i is a string
i := pChar(msg.LParam);
when it gets to the main thread i looks like this in the debugger:
'0100 0.50000 LSB0288.588.5SCAN EDG'#0
How can I correct this?
You are posting the contents of a String variable that is local to the thread procedure that is calling PostMessage(). If the String goes out of scope and gets freed before the main thread processes the posted message, the memory will be garbage.
You need to either:
1) use SendMessage() instead of PostMessage() so the String stays in scope until the message handler exits:
SendMessage(MainHandle, UM_CLONE, UM_756, LPARAM(PChar(s_out)));
2) dynamically allocate a String in memory, fill it as needed, post it, and then let the main message handler free it when it is done copying it:
var
s_out: PString;
New(s_out);
...
s_out^ := s_out^ + chr(dec);
...
if not PostMessage(MainHandle, UM_CLONE, UM_756, LPARAM(s_out)) then
Dispose(s_out);
.
var
ps: PString;
i: String;
ps := PString(msg.LParam);
i := ps^;
Dispose(ps);
PS: notice I also changed your Integer() cast to LPARAM(). This is very important if you ever upgrade to Delphi XE2 or later. PostMessage() and SendMessage() use LPARAM, not Integer. You can get away with it in Delphi 7 because LPARAM is an alias for Integer in that version, but that is not the case anymore in modern Delphi versions. LPARAM is an alias for NativeUInt now. LPARAM has always been an unsinged type in the Win32 API, Delphi just got it wrong in the early versions, but Embarcadero has really been pushing for type-correctness in the RTL/VCL since adding support for 64-bit and cross-platform development. If you don't match the right data types correctly, you can cause range checks errors and such at runtime.
Please help, how can i convert a real declared variable into a string one. Is there any function like IntToStr() ? RealToStr() function won't work.
There is a bunch of conversion routines in SysUtils unit, ie FloatToStr and other FloatTo* functions. Also see the Format function.
A really old method uses the 'Str' procedure, which has two parameters: the first is a real or integer, and the second is a string variable, into which the formatted number will be placed.
Examples:
i:= 1;
str (i, a); // a = '1'
r:= 1.5;
str (r:2, a); // a = '1.50'
It depends on the Delphi version you are using.
There is a FloatToStr in newer versions.
I think something like this would work...
procedure TestConversion;
Var
StringValue : String;
RealValue : Real;
begin
RealValue := 1 + 1.95;
Str(RealValue:0:2,StringValue);
// to display it in a label for example, it should be like this:
Label1.Caption := StringValue + ' is a Real Value!';
end;
so the output should be displayed in the Label1.Caption(as example) without problems like this:
2.95 is a Real value!