[delphi-users:3130] PCharの扱いについて

990 views
Skip to first unread message

ありい

unread,
May 13, 2013, 5:10:41 AM5/13/13
to delphi...@freeml.com
 皆様こんにちは。

 PCharの扱いについて悩みが生じましたので質問させて下さい。

 D7で作成したプログラムをXE2に移植中ですが、下記コードがXE2では例外が
発生します。

DrawText(DefaultPage.Handle, PChar('あ'), -1, rc1, cFormat);

#ハンドル、rc1、cFormatは適切に設定されています。

 XE2では PChar('あ') を PChar('あ' + #0) に修正すると動作します。

 DrawTextのドキュメントには「第3引数が-1の場合はNULL終止にしてちょ」
と書かれているようなので「PChar('あ')ってNULL終止じゃなかったっけ?」
...と思い、下記コードをデバッガで止めてD7、XE2でそれぞれメモリダンプを
確認しました。

procedure TForm1.Button1Click(Sender: TObject);
var
pc: PChar;
begin
pc := PChar('あ');
ShowMessage(pc);
end;

 D7ではpcに代入直後、$82 $A0 $00 $00 ...と期待通りの状態ですがXE2では
「参照できない値です」となり中身を確認できません。実行を続けると例外が
発生します。

#ちなみにXE2で pc := 'あ'; または PChar('あ' + #0) とすれば期待通りの
状態になります。

 質問は、「言語仕様上」、PChar('あ')のような記述はNGなのでしょうか?
ということです。今までD7では普通に使っていたのでXE2でダメって言われると
少し釈然としない気持ちです。

 元々D7でも宜しくないけど、たまたま動作していただけって事でしたら諦め
もつきます(笑)

#PChar('あ' + #0) が通るので、やっぱり若干の釈然としない感は残ると思い
ますが(笑)

#どうあれ現実問題として、XE2では先のコードのように例外が発生しないよう
に直して回るしかないのでしょうけど...(^^;

 今後の(D7含めた)コーディングスタイルを考え直すために、ご教示頂ければ
幸いです。

 以上、よろしくお願いします m(__)m

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
練習や試合の予定調整は「とっとと決め太郎」におまかせ!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQBzf
------------------------------------------------------[freeml byGMO]--

ありい

unread,
May 13, 2013, 6:03:03 AM5/13/13
to delphi...@freeml.com
 連投、失礼します m(__)m

 ぐぐっていて下記の記事を見つけました。

http://kakinotane.s7.xrea.com/delphi/faq/f006.html

 「文字列定数と PChar との関係は何か」の
 「3) 文字定数 の PChar 型へのキャストはうまく行かない(バグ?)」と同じ
現象のようです。

#XE2でpcのポインタを確認したところ、$3042(UTF-8の「あ」)なのでビンゴ!

 こちらはD5での現象とのことなので今のところ、

  D5 ×
  D7 ○
  XE2 ×
 
...という状況のようです。なんだかな~(^^;

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
新アプリ「freemlトーク」で気軽に会話を楽しもう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQCmB
------------------------------------------------------[freeml byGMO]--

hosokawa

unread,
May 13, 2013, 6:21:24 AM5/13/13
to delphi...@freeml.com
ありいさん

こんにちは
細川です。

記事の D5 で調べているのは 'a' という文字ですね。

D7 と XE2 で、ありいさんが調べられて居るのは 'あ' という文字ですよね?
この場合

---------------------------------------------------
■D7
文字コード:Shift-JIS
文字数:2(DBCS)

'あ'は2文字で構成される→ 文字列

---------------------------------------------------
■XE2(2009 以降)
文字コード:Unicode(UTF-16)
文字数:1

'あ'は1文字で構成される→ 文字
---------------------------------------------------

となります。
よって、XE2 で PChar('あ') は 'あ' という「文字」へのポインタになります。
長い文字列型は Null 終端ですが、文字は終端などありませんので、これが正しい結果
です。
Regards,
HOSOKAWA Jun
[S/G] SERIALGAMES Inc.
TEL: 03-5812-0980
FAX: 03-5812-0970
twitter: http://twitter.com/serialgames
twitter: http://twitter.com/flaver_sg
mailto: j...@serialgames.co.jp
fla:ver http://flaver.jp/
WebCapS http://www.serialgames.co.jp/fun.html#WebCapSeria


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
使い方はいろいろ♪一部のメンバーだけにMLメールを送ろう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQC3S
------------------------------------------------------[freeml byGMO]--

hosokawa

unread,
May 13, 2013, 6:31:31 AM5/13/13
to delphi...@freeml.com
ありいさん

細川です。

補足ですが、ポインタ操作をする場合は、そのポインタ・ポインタを得たいデータ型は
明示すべきです。

つまり、先ほどの場合は

PChar(String('あ'))

とすれば、防げます。

同様に、下記の様に Dispose に対して型無しポインタを渡すと、そのポインタが指し
ている変数の終了処理が実行されません。

procedure Exsample;
type
PFoo = ^TFoo;
TFoo = record
A: String; // 長い文字列(4バイトのポインタとなる)
B: Integer; // 4バイト
end;
var
Foo: PFoo;
Ptr: Pointer;
begin
New(Foo);
try
Ptr := Foo;
finally
// 8バイトのメモリ空間は解放されるが、長い文字列の解放ができない。
Dispose(Ptr);

// 正しくは、このように型を指定してやる必要がある
Dispose(PFoo(Ptr));
end;
end;
メンバーで使える掲示板を活用しよう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQCCN
------------------------------------------------------[freeml byGMO]--

中村@ブレーン

unread,
May 13, 2013, 9:00:43 PM5/13/13
to delphi...@freeml.com
中村@ブレーンです。

Delphi 1 に最初に触れたとき、文字と文字列の区別が不明瞭な仕様に
おどろいた覚えがあります。

以来、一文字の文字列は慎重に扱うようにしています。

私は、文字列として扱いたい時は、 String型の変数に入れてしまうことが多いですね。

式の中でテンポラリに String 型を作って、その参照を保持しないのは、
ちょっと怖い気がします。

ありい さんは書きました:
> D7で作成したプログラムをXE2に移植中ですが、下記コードがXE2では例外が
>発生します。
>
> DrawText(DefaultPage.Handle, PChar('あ'), -1, rc1, cFormat);

----------
東京都 日野市 中村拓男


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メールだけでみんなを招待できる便利機能♪
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQKp8
------------------------------------------------------[freeml byGMO]--

ありい

unread,
May 13, 2013, 9:57:41 PM5/13/13
to delphi...@freeml.com
 細川さん、ご返信ありがとうございます。

> ■D7
> 文字コード:Shift-JIS
> 文字数:2(DBCS)
>
> 'あ'は2文字で構成される→ 文字列
>
> ---------------------------------------------------
> ■XE2(2009 以降)
> 文字コード:Unicode(UTF-16)
> 文字数:1
>
> 'あ'は1文字で構成される→ 文字

 ご指摘の通りですね(^^; D7では文字列、XE2では文字なのを
見落としていました。

> よって、XE2 で PChar('あ') は 'あ' という「文字」へのポインタになります。
> 長い文字列型は Null 終端ですが、文字は終端などありませんので、これが正しい結果
> です。

 こちらは、

(1) pc := PChar('あ');

 「PChar('あ') は 'あ' という「文字」へのポインタ」ではなく、
pc に'あ'の文字コード($3042)がキャストされて、アドレスとして
セットされる

(2) ShowMessage(pc);

 pcを参照した時に$3042を見に行くのでアドレス違反が発生する

...のだと思いますが、如何でしょうか。

#「文字は終端などありませんので」以前に、参照してはいけない
アドレスを指定してしまっている、という動きに見えます。

 つまりDelphiのバージョンに関わらず、

pc := PChar('(1文字)'); // 1文字 = Char
// Charの文字コードをPCharにアドレスとしてキャスト

 →文字コードのアドレスを見に行くからアドレス違反に

pc := PChar('(文字列)'); // 1文字 + ''、''のみは文字列となる
// 正しいポインタが得られる

...と理解しました。

 そして正直、理解はできるけど紛らわしいな~って思います(^^;

#「Charの文字コードをPCharにアドレスとしてキャスト」する用が
それほど一般的に存在しますかね~~~?...って点でシンタックス
シュガーで必ず文字列にしてくれる方が混乱しないような...

##本当に「Charの文字コードをPCharにアドレスとしてキャスト」
が必要な人は pc := Pointer('A'); ってすれば良いと思うし...(^^;

 ともあれ、私の疑問、

『「言語仕様上」PChar('あ')のような記述はNGなのでしょうか?』

...は「NGではないが避けるべきだ」という結論に達しました。

> 補足ですが、ポインタ操作をする場合は、そのポインタ・ポインタを得たいデータ型は
> 明示すべきです。
(中略)
> 同様に、下記の様に Dispose に対して型無しポインタを渡すと、そのポインタが指し
> ている変数の終了処理が実行されません。

 その通りだと思いました。

 補足と併せて、詳しい解説をありがとうございました!

 また何かありましたら宜しくお願いします m(__)m

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
練習や試合の予定調整は「とっとと決め太郎」におまかせ!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQLbx
------------------------------------------------------[freeml byGMO]--

ありい

unread,
May 13, 2013, 10:06:34 PM5/13/13
to delphi...@freeml.com
 中村さん、ご返信ありがとうございます。

> 以来、一文字の文字列は慎重に扱うようにしています。
>
> 私は、文字列として扱いたい時は、 String型の変数に入れてしまうことが多いですね。
>
> 式の中でテンポラリに String 型を作って、その参照を保持しないのは、
> ちょっと怖い気がします。

 今回の件で、物凄く怖い思いが出来ました(^^;

 今後はテンポラリを作成する形で行きたいと思います!

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
新アプリ「freemlトーク」で気軽に会話を楽しもう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQLiU
------------------------------------------------------[freeml byGMO]--

hosokawa

unread,
May 13, 2013, 10:45:25 PM5/13/13
to delphi...@freeml.com
ありいさん

細川です。

> (1) pc := PChar('あ');
>
>  「PChar('あ') は 'あ' という「文字」へのポインタ」ではなく、
> pc に'あ'の文字コード($3042)がキャストされて、アドレスとして
> セットされる

'あ' は、文字定数になるので、その通りですね。
失礼いたしました。

> #「Charの文字コードをPCharにアドレスとしてキャスト」する用が
> それほど一般的に存在しますかね~~~?...って点でシンタックス
> シュガーで必ず文字列にしてくれる方が混乱しないような...

「文字定数をアドレスにキャストする」という用途はほぼないと思いますが、言語仕様
として「文字」と「文字列」が区別されていないので、まあ仕方が無いかなあと思いま
す。
Regards,
HOSOKAWA Jun
[S/G] SERIALGAMES Inc.
TEL: 03-5812-0980
FAX: 03-5812-0970
twitter: http://twitter.com/serialgames
twitter: http://twitter.com/flaver_sg
mailto: j...@serialgames.co.jp
fla:ver http://flaver.jp/
WebCapS http://www.serialgames.co.jp/fun.html#WebCapSeria


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
使い方はいろいろ♪一部のメンバーだけにMLメールを送ろう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQLF3
------------------------------------------------------[freeml byGMO]--

ありい

unread,
May 13, 2013, 11:14:36 PM5/13/13
to delphi...@freeml.com
 細川さん、ご確認ありがとうございます。

> 「文字定数をアドレスにキャストする」という用途はほぼないと思いますが、言語仕様
> として「文字」と「文字列」が区別されていないので、まあ仕方が無いかなあと思いま
> す。

 同感です。これから直すにしても今更感がありますし(^^;

 言語仕様としては、理解・納得できました。

pc := PChar('A'); // NG
pc := PChar('AA'); // OK

 しかし上記のように並べて見ると、やっぱり「優しくない」印象を
受けます...

#そして使い道は「Delphi言語マニアッククイズ」のネタぐらいしか
思い浮かびません(^^;

 因みに先のコードは描画の印字高さを取得するのに使っていました。
だから'あ'でいいや...って感じでした。その感覚でAPIに渡すときに
PChar括りをして... >最初の投稿に戻る

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メンバーで使える掲示板を活用しよう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQMht
------------------------------------------------------[freeml byGMO]--

中村@ブレーン

unread,
May 13, 2013, 11:22:47 PM5/13/13
to delphi...@freeml.com
あれ、うまく伝わったかな?

例えば

PChar(String('あ'))

と書いた場合、String('あ') の参照を誰も保持していないので、
String('あ')の寿命がいつ尽きるかはっきりはっきりしません。
#文字列は参照がなくなると破棄されるのが Delphi言語の仕様。
#ひょっとすると寿命が無限大な定数文字列扱いかもしれないけどそれもはっきりしない。

なので念のため、PCharで文字列を使用するときは

s: string;
p: PChar;

s := 文字列の式;
p := PChar(s);
// pを使う処理。

なんてしてます。

ありい さんは書きました:
> 今後はテンポラリを作成する形で行きたいと思います!

----------
東京都 日野市 中村拓男


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メールだけでみんなを招待できる便利機能♪
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQMqu
------------------------------------------------------[freeml byGMO]--

ありい

unread,
May 14, 2013, 2:17:59 AM5/14/13
to delphi...@freeml.com
 中村さん、解説ありがとうございます。

#引用の順序を変更しています。

> なので念のため、PCharで文字列を使用するときは
>
> s: string;
> p: PChar;
>
> s := 文字列の式;
> p := PChar(s);

 そのように理解しています。最初のコード

DrawText(DefaultPage.Handle, PChar('あ'), -1, rc1, cFormat);

...を、

s := 'あ';
DrawText(DefaultPage.Handle, PChar(s), -1, rc1, cFormat);

...のように修正すれば良いと考えています。

 この修正により、PChar()が必ず文字列として評価されるようになり、
文字・文字列の混乱から抜け出せると思います。

> 今後はテンポラリを作成する形で行きたいと思います!

 s := 'あ'; のs を「テンポラリを作成」と表現しました。なんだか
紛らわしかったようで申し訳ありません m(__)m

#ここから、文字列の永続性の件について。

> PChar(String('あ'))
>
> と書いた場合、String('あ') の参照を誰も保持していないので、
> String('あ')の寿命がいつ尽きるかはっきりはっきりしません。
> #文字列は参照がなくなると破棄されるのが Delphi言語の仕様。
> #ひょっとすると寿命が無限大な定数文字列扱いかもしれないけどそれもはっきりしない。

 XE2のヘルプを読んで、最初は最適化か何かでいきなり破棄されて
いるのかな?などと一瞬思いました(^^;

#'あ' + #0のケースが動くので、その可能性はすぐに消しました。

 今回のようにスレッドなどが絡まないケースでは、とりあえずは
DrawText()が終わるまで永続していれば充分と思っていますが...(^^;

 仮に、

DrawText(DefaultPage.Handle, PChar(String('あ')), -1, rc1, cFormat);

...と書いたとして、String('あ')がDrawTextの処理中程度の永続も
保証されていない、という事でしょうか?

 例えばDelphiの関数で何か長時間の処理が行われるとします。

function LongTime(s: string): string;
begin
// (長時間の処理)

result := s + ' - すげー時間かかりました!';
end;

// 長時間の処理呼び出し
ShowMessage(LongTime('あいう'));

 この処理の呼び元の'あいう'が、LongTime関数の最後の行までは
永続しているだろう事は、感覚的に信じています。

 これが、

function LongTime(p: PChar): string;
begin
// (長時間の処理)

result := p + ' - すげー時間かかりました!';
end;

ShowMessage(LongTime(PChar(String('A'))));

 や、APIの呼び出しの場合は無保証ということでしょうか?

 最初に示して頂いた下記コードのように、s: string; p: PChar;
として別途宣言しないと、参照を保持した事にはならないという事で
しょうか?

> s: string;
> p: PChar;
>
> s := 文字列の式;
> p := PChar(s);

 くどくなってしまって本当に申し訳ないですm(__)m
 お答え頂けると幸いです m(__)m

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
練習や試合の予定調整は「とっとと決め太郎」におまかせ!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQP9W
------------------------------------------------------[freeml byGMO]--

ありい

unread,
May 14, 2013, 5:07:58 AM5/14/13
to delphi...@freeml.com
 度々恐れ入ります。

 D7ヘルプの「文字列依存性」に思いっきり書いてありました(^^;

[以下、D7ヘルプを引用]

proceduremy_func(x:string);
begin
//do something with x
some_proc(PChar(x));//文字列をPchar にキャストする
//some_proc プロシージャが文字列を必要とする間
//文字列が存続することを保証する必要がある
end;

[引用終了]

> ShowMessage(LongTime(PChar(String('A'))));
>
> や、APIの呼び出しの場合は無保証ということでしょうか?

...つまり、上記は「無保証」ということですね。

#というか「自力で保証しなさい」と。

 最後に、

var
s: string;
begin
s := 'あ';
DrawText(DefaultPage.Handle, PChar(s), -1, rc1, cFormat);

...という形では、保証した事にはならないのでしょうか??

 普段、文字列の永続性を気にする事は中々ないですが、考えると
ややこしくて面白いですね(^^;

#そう考えると、たまたま無事に動いているように見えるけれど、
本当は危ないコードが沢山ある気がしてきました(^^;

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
新アプリ「freemlトーク」で気軽に会話を楽しもう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQSaJ
------------------------------------------------------[freeml byGMO]--

高木太郎

unread,
May 14, 2013, 8:23:34 AM5/14/13
to delphi...@freeml.com
 こんばんは、イマジオムの高木です。

ありいさん:
>> ShowMessage(LongTime(PChar(String('A'))));
>>
>> や、APIの呼び出しの場合は無保証ということでしょうか?
>
> ...つまり、上記は「無保証」ということですね。
>
> #というか「自力で保証しなさい」と。

 PChar や PAnsiChar へのキャストした場合の文字列ポインタは、
その文の中では有効であることが保証されていたと思います。

 上記の例なら、ShowMessage(LongTime(PChar(String('A')))) から
抜けてくるまでは、string('A') へのポインタは有効であるはず
ですよ。


> var
> s: string;
> begin
> s := 'あ';
> DrawText(DefaultPage.Handle, PChar(s), -1, rc1, cFormat);
>
> ...という形では、保証した事にはならないのでしょうか??

 これはさらに安全です。 end に到達するか、s の内容が変更
されるまで、s へのポインタは保証されます。


>  普段、文字列の永続性を気にする事は中々ないですが、考えると
> ややこしくて面白いですね(^^;
>
> #そう考えると、たまたま無事に動いているように見えるけれど、
> 本当は危ないコードが沢山ある気がしてきました(^^;

 そんなに危ないことはないのですが、PChar でキャストした
ポインタを変数に代入して使いまわすような場合には注意が
必要です。 たとえばこんな場合です。

  pc := PChar(s);
  s:=s+'abc'; {<======= ここで pc が無効になるかも }
Hoge(pc); {<======= ここでアクセス違反? }

――――――――――――――――――――――――――――――――――――
株式会社イマジオム 代表取締役 高木太郎
〒316-0024 茨城県 日立市 水木町 1-11-10
電話:0294-28-0147
ファクシミリ:0294-28-0148
電子メール:tarou_...@imageom.co.jp
ホームページ:http://www.imageom.co.jp/


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
使い方はいろいろ♪一部のメンバーだけにMLメールを送ろう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jQUZH
------------------------------------------------------[freeml byGMO]--

中村@ブレーン

unread,
May 14, 2013, 11:35:34 PM5/14/13
to delphi...@freeml.com
中村です。

高木太郎 さんは書きました:
> PChar や PAnsiChar へのキャストした場合の文字列ポインタは、
>その文の中では有効であることが保証されていたと思います。
>

Delphi 5 で、コンパイルコードを追って見たことがありますが、後始末コードは関数の実行直後の位置に
埋め込まれるようでした。参照カウント型のオブジェクトはインターフェースの参照カウントなども含め、できるだけ
後始末を後ろに伸ばそうというコンパイラの意図は感じられたのですが・・・

でも
bar(PCHAR(長い文字列の式));

がうまく動かなかったという経験があったので(時期的には Delphi 4以前 バグ?)。

油断大敵かもしれません。D7の言語仕様にも記述はないし、避けたほうが無難だと思います。
#XEとかではありましたっけ? 未確認です。

----------
東京都 日野市 中村拓男


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メンバーで使える掲示板を活用しよう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jRdhN
------------------------------------------------------[freeml byGMO]--

ありい

unread,
May 15, 2013, 1:05:51 AM5/15/13
to delphi...@freeml.com
 高木さん、中村さん、ご返信ありがとうございます。

(高木さん)
>  PChar や PAnsiChar へのキャストした場合の文字列ポインタは、
> その文の中では有効であることが保証されていたと思います。
>
>  上記の例なら、ShowMessage(LongTime(PChar(String('A')))) から
> 抜けてくるまでは、string('A') へのポインタは有効であるはず
> ですよ。

 昨日までは、そう信じていましたが...

 「文字列依存性」のヘルプを読んだら確信が揺らぎました(^^;

[D7ヘルプより引用] ※XE2も同様の記載でした

たとえば,PChar 型の引数をとる関数を使用する場合などに,長い文字列をヌルで終わる文字列に変換する必要が生じることがあります。文字列を
PChar へキャストする必要がある場合は,結果として生ずるPChar の存続期間を管理しなければならないことに注意してください。長い文字列は参
照カウントされるため,文字列のPChar への型キャストによって,実際には参照カウントがインクリメントされずに文字列への依存が1つ増えま
す。参照カウントがゼロに到達すると,文字列への追加の依存がある場合でも,その文字列は破棄されてしまいます。型キャストPChar も,PChar
を渡したルーチンがまだPChar を使用している間に消えてしまいます。例を示します。

[引用終了]

#そして昨日引用のコードに続きます。

(高木さん)
>  これはさらに安全です。 end に到達するか、s の内容が変更
> されるまで、s へのポインタは保証されます。
....
>  そんなに危ないことはないのですが、PChar でキャストした
> ポインタを変数に代入して使いまわすような場合には注意が
> 必要です。 たとえばこんな場合です。

 了解しました(^^) 普段そのような使い方はしないので、必要に
なった際には気を付けたいと思います。

(中村さん)
> 油断大敵かもしれません。D7の言語仕様にも記述はないし、避けたほうが無難だと思います。
> #XEとかではありましたっけ? 未確認です。

 XE2の「文字列依存性」周りをチェックしましたが、D7と同じ事が
書いてありますので言語仕様の変更はないものと推測します。

 という事で「文字・文字列の混乱」と「文字列の永続性」の2つ
の観点から、今後は

ShowMessage(LongTime(PChar(String('A'))));

...という記述は避け、

var
s: string;
begin
s := 'A';
ShowMessage(LongTime(PChar(s)));

...の記述を心掛けたいと思います。

 細川さん、中村さん、高木さん、色々とありがとうございました!

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メールだけでみんなを招待できる便利機能♪
http://ad.freeml.com/cgi-bin/sa.cgi?id=jReze
------------------------------------------------------[freeml byGMO]--

中村拓男(自宅)

unread,
May 15, 2013, 1:37:30 AM5/15/13
to "delphi-users@freeml.com"
昔も似たような議論があったようですね。高岡さんの解析がすごい。

[Delphi-ML:64722] PChar(string) の有効性について
http://www2.big.or.jp/~osamu/Delphi/delphi-browse.cgi?index=064722


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
練習や試合の予定調整は「とっとと決め太郎」におまかせ!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jReMA
------------------------------------------------------[freeml byGMO]--

auemura

unread,
May 15, 2013, 1:41:28 AM5/15/13
to delphi...@freeml.com
上村です。

Delphi Forumの方に書いたのですがこちらにも。
Webからなのでスレッドが切れていたらすみません。

XE3のヘルプですが文字列型のページ(http://docwiki.embarcadero.com/RADStudio/XE3/ja/%E6%96%87%E5%AD%97%E5%88%97%E5%9E%8B
に「Delphi 文字列と NULL 終端文字列の混在」という項目で
「UnicodeString 型または AnsiString 型の変数をポインタにキャストする場合は、その変数が新しい値を代入されるかスコープから外れるまで、そのポインタは有効です。他の任意の文字列式をポインタにキャストする場合、そのポインタは、型キャストが実行されるステートメント内でのみ有効です。」
という記述があるので、高木さんの言われているように
ShowMessage(LongTime(PChar(String(''A''))))
から抜けるまではポインタは有効であると思います。
新アプリ「freemlトーク」で気軽に会話を楽しもう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jReQC
------------------------------------------------------[freeml byGMO]--

中村拓男(自宅)

unread,
May 15, 2013, 2:38:17 AM5/15/13
to delphi-users
>他の任意の文字列式をポインタにキャストする場合、そのポインタは、型キャストが実>行されるステートメント内でのみ有効です。

ありがとうございます。現在のDelphiでは大丈夫そうですね。

2013年5月15日 14:41 auemura <delphi...@freeml.com>:
--
----------
Takuo Nakamura from Hino City Tokyo



MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
使い方はいろいろ♪一部のメンバーだけにMLメールを送ろう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jRfDH
------------------------------------------------------[freeml byGMO]--

ありい

unread,
May 15, 2013, 6:47:56 AM5/15/13
to delphi...@freeml.com
 上村さん、ご返信ありがとうございます。

> という記述があるので、高木さんの言われているように
> ShowMessage(LongTime(PChar(String(''A''))))
> から抜けるまではポインタは有効であると思います。

 了解しました! ほっとできる情報です(^^)

 コーディングスタイルに関しては、文字・文字列混在の問題が
残っているので、前記のままでと考えております。

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メンバーで使える掲示板を活用しよう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jRjyd
------------------------------------------------------[freeml byGMO]--

とんぼ

unread,
May 16, 2013, 1:40:02 AM5/16/13
to delphi...@freeml.com
一旦、終わったようですが・・・・
コンパイラのバグって事ですよね?
Char型にPCharでキャストできるって・・・
やる方が悪いのですかね?

*Delphi7です。

procedure TForm1.Button1Click(Sender: TObject);
var
pc : PChar;
Msg : String[11];
ch : Char;
begin
pc := PChar('A');
ShowMessage(pc); // 実行時エラー
ch := 'A';
pc := PChar(ch);
ShowMessage(pc); // 実行時エラー
Msg := 'AA';
pc := PChar(Msg); // コンパイルエラー
ShowMessage(pc);
pc := PChar(String(Msg)); // Stringに型変換
ShowMessage(pc); // エラーなし
end;
MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
新アプリ「freemlトーク」で気軽に会話を楽しもう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jRvn4
------------------------------------------------------[freeml byGMO]--

hosokawa

unread,
May 16, 2013, 1:43:48 AM5/16/13
to delphi...@freeml.com
とんぼさん

こんにちは。
細川です。

バグでは無いですね。
言語仕様通りの動きです。
Regards,
HOSOKAWA Jun
[S/G] SERIALGAMES Inc.
TEL: 03-5812-0980
FAX: 03-5812-0970
twitter: http://twitter.com/serialgames
twitter: http://twitter.com/flaver_sg
mailto: j...@serialgames.co.jp
fla:ver http://flaver.jp/
WebCapS http://www.serialgames.co.jp/fun.html#WebCapSeria


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
使い方はいろいろ♪一部のメンバーだけにMLメールを送ろう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jRvr4
------------------------------------------------------[freeml byGMO]--

ありい

unread,
May 16, 2013, 2:00:39 AM5/16/13
to delphi...@freeml.com
 とんぼさん、こんにちは。

> 一旦、終わったようですが・・・・
> コンパイラのバグって事ですよね?

 いいえ、仕様という事です。

 前の投稿で書いた、下記2行ですが、

> pc := PChar('A'); // NG
> pc := PChar('AA'); // OK

 NGというのは「私の」期待通りにならない...という意味合いです。

#「私」同様に、期待される方も多いだろうとは思いますが。

 書き換えると、

pc := PChar(「文字」型);
// 'A'は「文字」型:「文字」の内容をアドレスとしてキャストする

pc := PChar(「文字列」型);
// 'AA'は「文字列」型:「文字列」のポインタ先頭のアドレスを返す

...となります。

 そしてPChar(「文字」型)が『「文字」の内容をアドレスとして
キャストする』のは、正しい動作と言えます。

#通常のキャストに期待する動作として「正しい」のです。

 ただ、

pc := PChar('A');
pc := PChar('AA');

...と並べて見た時に、上が「文字」のキャストで下が「文字列」の
キャストというのは、とっても紛らわしい且つ判りにくいね~...
ってお話なのです。

> Char型にPCharでキャストできるって・・・
> やる方が悪いのですかね?

 まぁ、普通やらんよね... とは思いますし、シンタックスシュガー
で良きに計らってくれた方が嬉しかったねぇ... という思いを、前の
投稿には込めているつもりです。

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メンバーで使える掲示板を活用しよう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jRv4T
------------------------------------------------------[freeml byGMO]--

ken

unread,
May 16, 2013, 4:07:03 AM5/16/13
to delphi...@freeml.com
こんにちは。
似たような報告がQualityCentralに以前ありましたが、As Designed で Close されております。
[AV in OutputDebugString]
http://qc.embarcadero.com/wc/qcmain.aspx?d=99583

--
高橋智宏

とんぼ wrote:
> 一旦、終わったようですが・・・・
> コンパイラのバグって事ですよね?
> Char型にPCharでキャストできるって・・・
> やる方が悪いのですかね?
>
> *Delphi7です。
>
> procedure TForm1.Button1Click(Sender: TObject);
> var
> pc : PChar;
> Msg : String[11];
> ch : Char;
> begin
> pc := PChar('A'); ShowMessage(pc); // 実行時エラー
> ch := 'A';
> pc := PChar(ch); ShowMessage(pc); // 実行時エラー
> Msg := 'AA';
> pc := PChar(Msg); // コンパイルエラー
> ShowMessage(pc);
> pc := PChar(String(Msg)); // Stringに型変換
> ShowMessage(pc); // エラーなし
> end;


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メールだけでみんなを招待できる便利機能♪
http://ad.freeml.com/cgi-bin/sa.cgi?id=jRxnw
------------------------------------------------------[freeml byGMO]--

とんぼ

unread,
May 16, 2013, 5:00:12 AM5/16/13
to delphi...@freeml.com
細川さん・ありいさん・Kenさん
早い回答頂き有難うございます。

皆さんのご指摘されている、下記URLに色々有るのですが

http://leed.issp.u-tokyo.ac.jp/~takeuchi/delphi/browse.cgi?index=064757&back=http%3A%2F%2Fkakinotane.s7.xrea.com%2Fdelphi%2Ffaq%2Ff006.html
http://edn.embarcadero.com/jp/article/38791
http://kakinotane.s7.xrea.com/delphi/faq/f006.html
http://www.watercolor-city.net/ct_delphi/delphi_tiburon/doc_unicode/implict_convert.htm


もう一つ理解できません。
型キャストって、ある変数を別の型でオーバーラップするだけだと思っているのです。

Char型にPCharでキャストすれば、サイズが違うのでエラーに成りそうな気がするのですが。
Delphiコンパイラーは、厳格な型チェックを行ってくれるのに
以下は、明らかに間違いですが、コンパイルは通ります。
ch:Char;//Byte;integer;int64の時も
p :pointer;
p := pointer(ch);
p := pchar(ch);

Charは、Byteと同義なので通ってしまう様な気がします。
Pointer型のキャストは、対象が数値型なら何でもいいみたいですね。

ちょっと不細工なコーディングですが、キャストに付いて理解してる事を試してみました。
procedure TForm1.Button3Click(Sender: TObject);
Type
ArChar = Array[0..10] of Byte;
var
st : string;
pc : PChar;
sa : String;
begin
St := '01234567890';
St := '01234';
sa := intTohex(integer(@st), 8);
ShowMessage('文字列変数のアドレス= '+sa);
sa := intTohex(integer(pointer(st)^), 8);
ShowMessage('文字列変数の指す文字列へのアドレス= '+sa);
ShowMessage('文字列変数の指す文字列へのアドレスの実態= '
+intToHex(ArChar(pointer(st)^)[0], 2)
+intToHex(ArChar(pointer(st)^)[1], 2)
+intToHex(ArChar(pointer(st)^)[2], 2)
+intToHex(ArChar(pointer(st)^)[3], 2)
+intToHex(ArChar(pointer(st)^)[4], 2)
+intToHex(ArChar(pointer(st)^)[5], 2)
+intToHex(ArChar(pointer(st)^)[6], 2)
+intToHex(ArChar(pointer(st)^)[7], 2)
+intToHex(ArChar(pointer(st)^)[8], 2)
+intToHex(ArChar(pointer(st)^)[9], 2)
+intToHex(ArChar(pointer(st)^)[10], 2));
// 何故か、567890が無くなります。 
St := '01234567890';
sa := intTohex(integer(@st), 8);
ShowMessage('文字列変数のアドレス= '+sa);
sa := intTohex(integer(pointer(st)^), 8);
ShowMessage('文字列変数の指す文字列へのアドレス= '+sa);
ShowMessage('文字列変数の指す文字列へのアドレスの実態= '
+intToHex(ArChar(pointer(st)^)[0], 2)
+intToHex(ArChar(pointer(st)^)[1], 2)
+intToHex(ArChar(pointer(st)^)[2], 2)
+intToHex(ArChar(pointer(st)^)[3], 2)
+intToHex(ArChar(pointer(st)^)[4], 2)
+intToHex(ArChar(pointer(st)^)[5], 2)
+intToHex(ArChar(pointer(st)^)[6], 2)
+intToHex(ArChar(pointer(st)^)[7], 2)
+intToHex(ArChar(pointer(st)^)[8], 2)
+intToHex(ArChar(pointer(st)^)[9], 2)
+intToHex(ArChar(pointer(st)^)[10], 2));

Pc := Pchar(st);
sa := intTohex(integer(pointer(pc)^), 8);
ShowMessage('PCHAR変数の指す文字列へのアドレス= '+sa);
ShowMessage('PCHAR変数の指す文字列へのアドレスの実態= '
+intToHex(ArChar(pointer(Pc)^)[0], 2)
+intToHex(ArChar(pointer(Pc)^)[1], 2)
+intToHex(ArChar(pointer(Pc)^)[2], 2)
+intToHex(ArChar(pointer(Pc)^)[3], 2)
+intToHex(ArChar(pointer(Pc)^)[4], 2)
+intToHex(ArChar(pointer(Pc)^)[5], 2)
+intToHex(ArChar(pointer(Pc)^)[6], 2)
+intToHex(ArChar(pointer(Pc)^)[7], 2)
+intToHex(ArChar(pointer(Pc)^)[8], 2)
+intToHex(ArChar(pointer(Pc)^)[9], 2)
+intToHex(ArChar(pointer(Pc)^)[10], 2));
ShowMessage('PCHAR変数'+pc);
ShowMessage('参照カウンタ'+intToStr(PLongInt(pc - SizeOf(LongInt)-
SizeOf(LongInt))^));
ShowMessage('文字数'+intToStr(PLongInt(pc - SizeOf(LongInt))^));
end;

----- Original Message -----
MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
練習や試合の予定調整は「とっとと決め太郎」におまかせ!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jRydC
------------------------------------------------------[freeml byGMO]--

ありい

unread,
May 16, 2013, 6:08:17 AM5/16/13
to delphi...@freeml.com
 とんぼさん、こんばんは。

 私の拙い理解ですが...

> もう一つ理解できません。
> 型キャストって、ある変数を別の型でオーバーラップするだけだと思っているのです。

 その通りだと思います。

 私が引っかかった問題も、

p := PChar('A'); // 'A'はChar。
//メモリのどこかに配置された'A'($41)がアドレスとしてキャストされる
//そしてp を使うとアドレス違反になる

 どこかのメモリ
  $41 ←これをキャスト

 p を利用しようとすると、$41のアドレスを見に行ってアドレス違反。

p := PChar('AA'); // 'AA'はString。
//Stringはポインタなので、'AA'はメモリのどこかに配置されて、そのアドレスを
//指したポインタのアドレスがキャストされる
//なので利用可能なp になる

 どこかのメモリ
  $XXXXXXXX ←これをキャスト
  (文字列'AA'が格納されているアドレス)

 $XXXXXXXX
  $41 $41 $0(null) ...

 p は$XXXXXXXXのアドレスを見に行って無事に'AA'を取得できる。


 私はうっかりCharをキャストしたp を使おうとしてハマった訳です(^^;

#発端は'あ'ですが、D7の'あ'(String)とXE2の'あ'(Char)を、UNICODEの
事をスカっと忘れて挙動が違う!と思ってしまったことです(^^;

> Char型にPCharでキャストすれば、サイズが違うのでエラーに成りそうな気がするのですが。
> Delphiコンパイラーは、厳格な型チェックを行ってくれるのに
> 以下は、明らかに間違いですが、コンパイルは通ります。

 型キャストに関しては「プログラマ責任で」自由にさせてくれている
側面があると思います。

#DLLの呼び出しなど、させてくれないと厳しい局面も存在するので...

 この部分については自己責任となり、その代わり"厳格な型チェック"
の制約から逃れさせてくれるのです。

 下記は、大きい幅から狭い幅にキャストしますが、特に警告もエラー
もなく実行できます。

#バイト幅(大)←→(小) を「自己責任で」キャスト可能です。

var
ch:Char; p :pointer;
begin
p := Pointer(Form1);
ch := Char(p);
ShowMessage('p:' + IntToHex(Integer(p), 8) + ' ch:' + IntToHex(Integer(ch), 8));
end;

> ちょっと不細工なコーディングですが、キャストに付いて理解してる事を試してみました。

 ごめんなさい、こちらは何を確認したかったのかが判りませんでした m(__)m

 少しでも参考になれば幸いです。

#また間違いなどありましたら、ご指摘頂ければ幸いです m(__)m

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
新アプリ「freemlトーク」で気軽に会話を楽しもう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jRzjL
------------------------------------------------------[freeml byGMO]--

hosokawa

unread,
May 16, 2013, 7:21:12 AM5/16/13
to delphi...@freeml.com
とんぼさん

細川です。

すみません、ちょっと何が理解できないのが捉え切れていない部分もありますが……

"語弊" を恐れずに書くと、

型キャストとは「型の変換」ではありません。
型キャストとは「使用するバイト数の変換」です。

型キャストは、ある型を別の型のサイズに合わせます。
そのとき、元の型が大きい場合、上位バイトを破棄します。
逆に小さい場合は、上位バイトを 0 埋めします。

ここで、次のような変数があるとします。

C: Char;
I: Integer;

ここで C に I を代入

C := I;

このとき、I の上位3バイトは廃棄されます。
イメージとしては

□ := ■■■■ // 1 byte の箱に 4 byte のデータを入れる



こんな感じです。
逆に

I := C;

このときは、足りない3バイトが 0 で埋められます。

□□□□ := ■ // 4 byte の箱に 1 byte のデータを入れる

□□□■

では、浮動小数点の変数は、どうなるでしょうか。

D: Double; // 8byte
S: Single; // 4byte
I: Integer; // 4byte

D := Double(S);
D := Double(I);

S := Single(D);
S := Single(I);

I := Integer(D);
I := Integer(S);

いずれもコンパイルエラーになります。
それは、実数型を異なる型にバイトデータとして代入すると、無意味な値になるためで
す。

    ┏━━━┓
□ :=  ┃ ┃ // 分割不可能なので他の箱には入らない
    ┗━━━┛

では、キャスト式を外した以下の代入文はどうなるでしょうか。

D := I;
S := I;
D := C;
S := C:

これは成功します。
なぜかというとキャストとは違い、サイズを変更する訳では無いからです。

以上の結果より、キャストの成立条件がわかります。
それは、型の構造がバイトデータと直結していること、です。

これらより、一見不思議な以下の式は成功します。

I := Integer('A'); // 0x41 が入る
I := Integer(True); // 1 が入る
I := Integer(Pointer('A')); // 0x41 が入る

これで、もうおわかりでしょうが

P: Pointer;

P := PChar('A'); // 1 byte の値を 4 byte の値に変換する

これも、当然成立するのです。

ですが、1つだけ上記の条件に反しているのにキャストできる型あります。
それが UnicodeString / AnsiString 型、いわゆる長い文字列型です。
長い文字列型を PChar へ変換すると、コンパイラマジックにより文字列を指すポイン
タになります。
多分、この機構があることで、混乱が起きているのだと思います。

ちなみに、長い文字列型ができる前の ShoftString は array of char と互換性があり
ました。
この辺りの「便利機構」は全て C/C++ の Null-terminated String と簡単に互換させ
るための仕組みです。

キャストについては x86 の機械語レベルで考えると非常に判りやすい(RAX, EAX, AX,
AL の関係)と思いますが、より長くなってしまうので割愛します。

Regards,
HOSOKAWA Jun
[S/G] SERIALGAMES Inc.
TEL: 03-5812-0980
FAX: 03-5812-0970
twitter: http://twitter.com/serialgames
twitter: http://twitter.com/flaver_sg
mailto: j...@serialgames.co.jp
fla:ver http://flaver.jp/
WebCapS http://www.serialgames.co.jp/fun.html#WebCapSeria


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
使い方はいろいろ♪一部のメンバーだけにMLメールを送ろう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jR0lH
------------------------------------------------------[freeml byGMO]--

とんぼ

unread,
May 16, 2013, 7:34:35 AM5/16/13
to delphi...@freeml.com
あいりさん
こんばんは。

最初の、あいりさんの問題提起は早い内に解決してたのは解っていたのですが
キャスト他の寿命に付いての話に展開していたので書いてみました。

Pchar型変数は、ポインター型変数で変数の大きさ(占有Byte数)決まっているのに
Byte・・・int64の変数にキャストできるのは、コンパイラーでチェックできると考えて
Delphiのバグではないかと思っただけです。

あいりさんがおっしゃる様に、便利な事有るのかは解りませんが、
pointer変数(PChar変数)はPointer型であって32Bit環境では、4Byteで変わりません。

なのに何故、Byteにキャストがコンパイルエラーに成らないのかがやっぱり疑問です。


理解悪くてすみません。

----- Original Message -----
From: "ありい" <delphi...@freeml.com>
To: <delphi...@freeml.com>
MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
メンバーで使える掲示板を活用しよう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jR0xF
------------------------------------------------------[freeml byGMO]--

ありい

unread,
May 16, 2013, 9:07:48 PM5/16/13
to delphi...@freeml.com
 とんぼさん、おはようございます。

> Pchar型変数は、ポインター型変数で変数の大きさ(占有Byte数)決まっているのに
> Byte・・・int64の変数にキャストできるのは、コンパイラーでチェックできると考えて
> Delphiのバグではないかと思っただけです。
(順序変更)
> なのに何故、Byteにキャストがコンパイルエラーに成らないのかがやっぱり疑問です。

 疑問点が理解できました。
 前にも書きましたが、これはDelphi「あえて」の仕様です。

> あいりさんがおっしゃる様に、便利な事有るのかは解りませんが、

 私が遭遇した事案ですが...

 OPOSドライバで、内部処理は参照渡しに対してLongBool(4byte)で書き
込むのに、COMインターフェース上はWordBool(2byte)を要求する(という
かCOMにLongBoolがない)という問題がありました。

 生成されたタイプライブラリで動かすと、WordBoolに対してLongBoolを
書き込むので必ずアドレス違反が発生...(^^;

 そこで呼び元ではLongBoolを定義、渡す際にWordBoolに偽装するという
細工が必要になりました。

http://ari-memo.seesaa.net/article/232626145.html

 ここで仮にガチガチに型を縛られてしまっていたら、この問題は恐らく
解決できなかったか、もっと苦しむ事になったと思います。

 繰り返しになりますが、キャストはプログラマを型の制約から解放して
くれる、その代わり「自己責任」となる仕組みなのです。

 だから用がなければ使わないに越したことはないと思いますよ。その方
が安全なプログラミングが出来ますので...

 そして、それだけじゃ済まないケースがある事をDelphiの製作者が理解
しているから、キャストのような「安全でない」仕組みも備わっている、
という事だと思います。

 少しでも、とんぼさんの参考になれば幸いです m(__)m

ありい


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
練習や試合の予定調整は「とっとと決め太郎」におまかせ!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jR6Yu
------------------------------------------------------[freeml byGMO]--

とんぼ

unread,
May 16, 2013, 9:50:04 PM5/16/13
to delphi...@freeml.com
細川さん・あいりさん
おはようございます。

私の頭は、20年前で停止した状態にあったみたいですね。
型(アドレス)形式でしかキャスト処理をやった事ないので、
integer(char)みたいなコーディングで、昔、痛い目に会った記憶思い出しました。
以来、型(アドレス)形式のキャストしか使ってないですね。

integer(char)が型変換してくれるって、無意識に使用してる事有りましたが
改めて思い出してみると、頭の中は、型(アドレス)形式を踏み外さないようにコーディングしています。

お騒がせしました。

----- Original Message -----
From: "hosokawa" <delphi...@freeml.com>
To: <delphi...@freeml.com>
MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
新アプリ「freemlトーク」で気軽に会話を楽しもう!
http://ad.freeml.com/cgi-bin/sa.cgi?id=jR7AD
------------------------------------------------------[freeml byGMO]--

Reply all
Reply to author
Forward
0 new messages