2013年8月16日金曜日

Lazarus で String と PChar を扱う場合の注意

環境:Lazarus IDE v1.0.10,FPC v2.6.2,-MDelphi

文字列関係は、まあいろいろあって面倒くさい。

Delphi 5 - 2007 → Lazarus で String と PChar を扱う場合の注意。

function test: string;
var
 tmp: String;
 s,e,p: PChar;

begin

 tmp:='AA';

 s:= @tmp[1];          //開始
 e:= s + length(tmp);  //終了
 p:= s;                //走査用

 while p<e do
 begin
   if p^='A' then
      p^:='B'
   else
      p^:=chr(byte(p^)+1); //C,D,E,F....
   inc(p);
 end;
 Result:= tmp;
end;

この関数は、毎回 "BB" を返すことを想定している。
関数を呼び出す度に tmp は "AA" になるので結果は "BB"になる。

Delphi 5 だと想定通りの結果を返す。

何度呼び出そうとも tmp は毎回1度は"AA"になり、結果 Result は "BB" となる。

ところが Lazarus だと 1回目のtest()呼び出し時には 結果 Result は "BB" となるが 2回目以降 の test()呼び出し時には、結果 Result は "CC"~ になってしまう。
つまり "AA" それ自体が "AA" ではなくなってしまう。

function test: string;
const
  def='AA';
begin
  tmp:=def;

としても、同じように、2回目以降、結果 Result は "CC"~ が結果となる。
(const は interface部に書いても同様の結果。)
つまり const def ='AA' としようとも"AA"自体が書き換えられてしまう。

これを回避するには

tmp:='AA';


ではなく



move('AA',pointer(tmp)^,2);

のように毎回 "AA" を別の所(固定値のアドレスが異なるように配慮し)与える必要があるようだった。

このようにも書ける。

p:=s;
while p<e do

begin
  p^:='A';
  inc(p);
end;


調べたところ単に


tmp:=PChar('AA');

とするだけで期待通りの動作になる。


とりあえず、固定値を定義して、PCharとかpointerでアクセスして値を書き換えるようなコードはすべて注意する必要がある・・・という事。

ということで、私と同じようにハマったらこの記事を思い出してみてください。

以上。

http://bugs.freepascal.org/view.php?id=22534

http://bugs.freepascal.org/view.php?id=10341

ということらしい。

なんだか Pascal の伝統が1つ失われたような気がしないでもない。

p.s.
この記事は、別の私的なブログから改訂しつつ移動しました。

0 件のコメント:

コメントを投稿