Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2012-04

Strange Problem!

8 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Amir

Hi,

I have encountered a strange problem. I developed the following function as a faster implementation for IntToStr:

function MyIntToStr (n: Int64): AnsiString;
const
   MaxLength= 16;
var
   CurDigit: PChar;
   Sign: Boolean;
begin
   Result:= '                ';
   CurDigit:= @(Result [MaxLength]);
   Sign:= False;
   if n< 0 then
   begin
     Sign:= True;
     n:= -n;
   end;
   if n= 0 then
     Exit ('0');
   while n 0 do
   begin
     CurDigit^:= Char (48+ n mod 10);
     Dec (CurDigit);
     n:= n div 10;
   end;
   if Sign then
     CurDigit^:= '-';
end;

And then, in main , I have

begin
   WriteLn (MyIntToStr (5458));
   WriteLn (MyIntToStr (5458));
   WriteLn (MyIntToStr (2));
end.

The output is:
5458
5458
5452

I tried gdb and noticed the first line of MyIntToStr (Result:= '...') is
not executed correctly. I am using fpc-2.6.0-1.x86_64

Thanks,
Amir

Reply by email

Flávio Etrusco
to Amir

You program isn't valid. You're assigning a constant (a global variable) to Result, so you shouldn't change it's contents through a pointer (if you change it normally the compiler+RTL will COW the value).
You can use a shortstring or:
Result := StringOfChar(' ', 16);

-Flávio

Amir wrote 65 lines
Hi, I have encountered a strange problem. I developed the following function as a faster implementation for IntToStr: function MyIntToStr (n: Int64): AnsiString; const  MaxLength= 16; var  CurDigit: PChar;  Sign: Boolean; begin  Result:= '                ';  CurDigit:= @(Result [MaxLength]);  Sign:= False;  if n< 0 then  begin    Sign:= True;    n:= -n;  end;  if n= 0 then    Exit ('0');  while n 0 do  begin    CurDigit^:= Char (48+ n mod 10);    Dec (CurDigit);    n:= n div 10;  end;  if Sign then    CurDigit^:= '-'; end; And then, in main , I have begin  WriteLn (MyIntToStr (5458));  WriteLn (MyIntToStr (5458));  WriteLn (MyIntToStr (2)); end. The output is:           5458           5458           5452 I tried gdb and noticed the first line of MyIntToStr (Result:= '...') is not executed correctly. I am using fpc-2.6.0-1.x86_64 Thanks, Amir _______________________________________________ fpc-devel maillist  -   http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Amir
to Flávio Etrusco

I am not sure what you mean be "your program is not valid". I am initializing Result. Modify its content and ...

I already observed that if I use either shortstring or use indices to modify Result, this problem does not occur. But still I can't see what causes this problem. And I believe it is a bug, isn't it?

Amir

Flávio Etrusco wrote 75 lines
You program isn't valid. You're assigning a constant (a global variable) to Result, so you shouldn't change it's contents through a pointer (if you change it normally the compiler+RTL will COW the value). You can use a shortstring or: Result := StringOfChar(' ', 16); -Flávio On Tue, Apr 24, 2012 at 7:36 PM, Amir wrote: > Hi, > > I have encountered a strange problem. I developed the following function as > a faster implementation for IntToStr: > > > function MyIntToStr (n: Int64): AnsiString; > const > MaxLength= 16; > > var > CurDigit: PChar; > Sign: Boolean; > > begin > Result:= ' '; > > CurDigit:= @(Result [MaxLength]); > Sign:= False; > > if n< 0 then > begin > Sign:= True; > n:= -n; > > end; > if n= 0 then > Exit ('0'); > > > while n 0 do > begin > CurDigit^:= Char (48+ n mod 10); > Dec (CurDigit); > n:= n div 10; > > end; > if Sign then > CurDigit^:= '-'; > > end; > > And then, in main , I have > > begin > WriteLn (MyIntToStr (5458)); > WriteLn (MyIntToStr (5458)); > WriteLn (MyIntToStr (2)); > > end. > > The output is: > 5458 > 5458 > 5452 > > I tried gdb and noticed the first line of MyIntToStr (Result:= '...') is not > executed correctly. I am using fpc-2.6.0-1.x86_64 > > > Thanks, > Amir > _______________________________________________ > fpc-devel maillist - > http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Ralf A. Quint
to Amir
Amir wrote 6 lines
I am not sure what you mean be "your program is not valid". I am initializing Result. Modify its content and ... I already observed that if I use either shortstring or use indices to modify Result, this problem does not occur. But still I can't see what causes this problem. And I believe it is a bug, isn't it?

Yes, a bug in your code. You never set the length of the result string anywhere.

Try to initialize the local string with

Result:= 'xxxxxxxxxxxxxxxx'

and then call your function with

WriteLn (MyIntToStr (11111));
WriteLn (MyIntToStr (2222));
WriteLn (MyIntToStr (333));
WriteLn (MyIntToStr (44));
WriteLn (MyIntToStr (5));

and see what happens...

Ralf

Reply by email

Jonas Maebe
to Amir: I am not sure what you mean be "your program is not valid". I am initializing Result.…
Amir wrote
I am not sure what you mean be "your program is not valid".

This is not valid when Result is an ansistring (or unicodestring), at least if you afterwards start writing to Result via the CurDigits pointer:

Result:= ' '; CurDigit:= @(Result [MaxLength]);

The reason is that ansistring is an implicit pointer type, unlike shortstring. Assigning a constant ansistring to Result means simply that a pointer to that series of spaces is copied into the Result pointer. If you then start modifying Result by hacking behind the compiler's back (which is what you do by using a pointer), the compiler cannot make a copy of that constant string and you start modifying the original constant data that was stored in the program. As a result, when you call the function again, the first assignment will assign whatever you stored in that constant during the previous execution of your function rather than a series of spaces.

The solution is to create a local copy of the constant before you start modifying it, which you can do by adding e.g. setlength(Result, length(Result)) after the first assignment.

In summary: do not use pointers unless you know in detail how the compiler implements a particular data structure. Using pointers is like using assembler: it can result in fast code, but it's an excellent way to shoot yourself in the foot.

Jonas

Reply by email

Martin
to Amir: I am not sure what you mean be "your program is not valid". I am initializing Result.…
Amir wrote 8 lines
I am not sure what you mean be "your program is not valid". I am initializing Result. Modify its content and ... I already observed that if I use either shortstring or use indices to modify Result, this problem does not occur. But still I can't see what causes this problem. And I believe it is a bug, isn't it?

Try the program at the end.
it will print "x" !!!

Strings a pointers. And they do "copy on write"

    s2 := s1;

means that s1 and s2 point to the same memory (not only the same content, but the same location in mem)

Internally the strig knows, that the memory is used by 2 strings

   s2[1] := 'a';

will trigger "copy on write". So s2 gets copied first then modified

using pchar does not trigger such copies.

so the cod modifies the constant you use for initialization.

try
UniqueString(Result);
program Project1;
function Foo: AnsiString;
begin
   Result:= '                ';
end;
var
   s: String;
   p: PChar;
begin
   s := Foo;
   p := @s[1];
   p^ := 'x';
   writeln(Foo);
readln;
end.

Reply by email

Amir
to Jonas Maebe: This is not valid when Result is an ansistring (or unicodestring), at least if you…
The reason is that ansistring is an implicit pointer type, unlike shortstring. Assigning a constant ansistring to Result means simply that a pointer to that series of spaces is copied into the Result pointer. If you then start modifying Result by hacking behind the compiler's back (which is what you do by using a pointer), the compiler cannot make a copy of that constant string and you start modifying the original constant data that was stored in the program. As a result, when you call the function again, the first assignment will assign whatever you stored in that constant during the previous execution of your function rather than a series of spaces.

Makes sense!
Why does fpc consider the constant string ' ' as an ansistring, not a shortstring? What fpc did, in this case, is not what I expected.

Amir

Reply by email

Jonas Maebe
to Amir
Amir wrote
> The reason is that ansistring is an implicit pointer type, unlike shortstring. Assigning a constant ansistring to Result means simply that a pointer to that series of spaces is copied into the Result pointer. If you then start modifying Result by hacking behind the compiler's back (which is what you do by using a pointer), the compiler cannot make a copy of that constant string and you start modifying the original constant data that was stored in the program. As a result, when you call the function again, the first assignment will assign whatever you stored in that constant during the previous execution of your function rather than a series of spaces. Makes sense! Why does fpc consider the constant string ' ' as an ansistring, not a shortstring?

Because your function is declared as returning an ansistring.

Jonas

Reply by email