-2

In older versions of Delphi, like D7, you could do like ord(s[i]) where s was a string, but trying this with an AnsiString results in an exception (access violation).

P.S. I was w/delphi 7 for a long time.

Here are the steps to reproduce the error: Create a new project and through a memo on the form (let it be memo1) than add the following code to the form create event handler:

procedure TForm1.FormCreate(Sender: TObject);
var u: ansistring;
begin
    u := 'stringtest';
    memo1.Lines.Add(inttostr(ord(u[2])));
end;

For me this code produces an AV.

Gonzalez
  • 681
  • 1
  • 11
  • 21
  • 1
    `string` in D7 *was* `AnsiString`. The behavior of indexing into an `AnsiString` in newer versions hasn't changed. – Remy Lebeau Apr 13 '16 at 21:42
  • 1
    This is simply untrue. You have an issue with your code if that's the case, but clearly without your code we can't tell you what that issue might be. – Ken White Apr 13 '16 at 22:30
  • I don't understand why it is impossible to show your code that produces the AV. Are you afraid that somebody steals your precious mindwork with all the bugs it includes? – Tom Brunberg Apr 14 '16 at 08:17
  • @Tom Brunberg it is possible and I've added the code to the question. I think I should do that at first and please excuse me for not doing so. – Gonzalez Apr 15 '16 at 07:55

1 Answers1

3

It does work with an ansistring, but you cannot read past the end of it and you must make sure the string is initialized.

function CharCode(const S: ansistring; pos: integer): byte;
begin
  if pos <= 0 then result:= 0
  //else if s='' then Result:= 0 //unassigned string;
  else if Length(s) < Pos then Result:= 0  //cannot read past the end.
  else Result:= Ord(s[pos]);
end;

Note that if s='' is the same as asking if pointer(s) = nil. An empty string is really a nil pointer.
This is probably why you where getting an access violation.

If you want to force the ansistring to be a certain length you can use SetLength(MyAnsistring, NewLength);

The length of the (ansi)string is variable. That means it grows and shrinks as needed. If you read past the end of the string you may get an access violation.
Note that you don't have to get an AV, the RTL leaves a bit of slack in its allocation; it usually allocates a slightly bigger buffer than requested, this is due to performance and architectural reasons.

The other reason why you may not get an AV if reading past the end of a string is that your program may own both the string buffer and whatever happens to be right next to it.

For this reason it is a good idea to enable range checking in debug mode {$R+} it adds extra checks to protect against reading past the end of structures.

The difference between shortstring and (ansi)string
A short string has a fixed length and it lives on the stack.
A long string (ansi or wide) is a pointer to a record that gets allocated on the heap; it looks like this:

type
  TStringRecord = record
    CodePage: word;     
    ElementSize: word; //(1, 2 or 4)    
    ReferenceCount: integer;    
    Length: Integer;    
    StringData: array[1..length(s)] of char;
    NullChar: char;
  end;

The compiler hides all these details from you.
see: http://docwiki.embarcadero.com/RADStudio/Seattle/en/Internal_Data_Formats

Johan
  • 74,508
  • 24
  • 191
  • 319
  • This is strange but that doesn't work: `u := ansistring('string');memo1.Lines.Add(inttostr(charcode(u,3)));` where u is declared as an ansistring. – Gonzalez Apr 13 '16 at 21:33
  • It is not likely that the reason has something to do with ranges, cause in the example above I am not trying to read past the end or get even close to it. – Gonzalez Apr 13 '16 at 21:37
  • @Gonzalez: define "doesn't work", because there is no possible way that example can be crashing with an AV on the call to `CharCode()` or `IntToStr()`. An AV would have to be on the call to `memo1.Lines.Add()` instead. What is the EXACT text of the error message that is being raised? – Remy Lebeau Apr 13 '16 at 21:45
  • @Gonzalez, it works ok on my machine. This runs just fine: `program Project76; {$APPTYPE CONSOLE} uses System.SysUtils; function CharCode(const S: ansistring; pos: integer): byte; begin if s='' then Result:= 0 //unassigned string; else if Length(s) < Pos then Result:= 0 //cannot read past the end. else Result:= Ord(s[pos]); end; var u: ansistring; begin u:= ansistring('string'); writeln(IntToStr(CharCode(u,3))); ReadLn; end.` – Johan Apr 13 '16 at 21:46
  • @Johan: You don't need to check for `if s='' then` if you check the `Length()` afterwards. The `Length()` of a blank/nil string is 0, and thus will be handled by the `if Length(s) < Pos then` check. You might want to change the statement to `if (Pos < 1) or (Pos > Length(s)) then` instead, though. – Remy Lebeau Apr 13 '16 at 21:48
  • @RemyLebeau, I put the `if s=''` in there to show that an empty string and nil and `''` are one and the same thing. – Johan Apr 13 '16 at 21:50
  • @Johan: it is redundant code in this case. You could just state that fact in a comment instead. – Remy Lebeau Apr 13 '16 at 21:51
  • @Johan A couple of minor quibbles: (1) Shortstring doesn't have to live on stack, the point is (unlike AnsiString) it _can_ live entirely on the stack (but it can also live on heap). (2) It is a good idea to enable range checking **full stop**. The performance overhead of runtime range checking sounds like a lot, but in reality it's usually only a much smaller percentage of overall runtime. It will only have an overall impact in a small number of locations where code is called thousands of times per second. If a profiler catches those locations; it's trivial to locally disable range checking. – Disillusioned Apr 14 '16 at 00:25