charとunsigned charとdeleteについて

解決


Bdasj  2008-05-07 00:28:17  No: 68254  IP: 192.*.*.*

以下のようにnewするときとdeleteするときでchar*の型が違っている場合
問題はないでしょうか?

void Func(unsigned char*);

char* c = new char[100];
strcpy(c, "xyz");

//引数としてunsigned char*を要求される関数を呼ぶ。
unsigned char* uc = reinterpret_cast<unsigned char*>(c);
Func(uc);

//char*をunsigned char*に強制キャストしたものを配列delete
delete[] uc;


charとunsigned char/singned charは型としては別のものだそうですが、
上記のように、Cの文字列操作関数がchar*を要求しているからまずは
char*で文字列を組み立て、別の関数がunsigned char*を要求しているから
char*をunsigned char*にキャストして、使い終わったから
unsigned char*のままdelete[]という動作は問題ないものでしょうか?

また、reinterpret_castはよほどのことがない限り使わない方がよいと
Effective C++などには書かれているのですが、
char*からunsigned char*またはsigned char*にキャストする際は、
static_castだとコンパイルが通りません。
この場合のreinterpret_castしかないのでしょうか?

よろしくお願いいたします。

編集 削除
Blue  2008-05-07 00:57:05  No: 68255  IP: 192.*.*.*

後半だけ。

>char*からunsigned char*またはsigned char*にキャストする際は、
>static_castだとコンパイルが通りません。
void*にstatic_castしてからやればいいのではないでしょうか?

unsigned char* uc = static_cast<unsigned char*>(static_cast<void*>(c));

編集 削除
Bdasj  2008-05-07 02:00:13  No: 68256  IP: 192.*.*.*

早速のご回答ありがとうございます。

>unsigned char* uc = static_cast<unsigned char*>(static_cast<void*>(c));

そういうやり方があったのですね。
勉強になります。
すみませんが一点お聞きしたいことがあるのですが、
こういう風にvoid*にキャストしてから改めて目的の型にキャストしなおすという
やり方はreinterpret_cast一発でやるよりも安全だったりするのでしょうか?

編集 削除
επιστημη  URL  2008-05-07 08:42:24  No: 68257  IP: 192.*.*.*

> char*をunsigned char*にキャストして、使い終わったから
> unsigned char*のままdelete[]という動作は問題ないものでしょうか?

お勧め致しかねます。PODだからまだマシだけども:
char* p = new ...
Foo* q = reinterpret_cast<Foo*>(p);
...
delete[] q;

Fooが仮想デストラクタ持ってたらぶっ飛ぶんじゃないかと。

編集 削除
tetrapod  2008-05-07 09:10:15  No: 68258  IP: 192.*.*.*

ISO/IEC 14882:1998 5.3.5 Delete (3)
In the second alternative (delete array) if the dynamic type of the object to be deleted
differs from its static type, the behavior is undefined.
とあるっす

reinterpret_cast 一発のほうが適切でっしゃろ、この場合
5.2.10 reinterpret_cast (7)
A pointer to an object can be explicitly converted to a pointer to an object of different type.

提示例では単に delete [] c; とすればよいわけで何の問題も無い

編集 削除
Bdasj  2008-05-08 22:53:10  No: 68259  IP: 192.*.*.*

To:επιστημη様

説明が足りませんでした。
今回の話はあくまで組み込み型の間でのことです。
他にも、int*で確保したものをunsigned int*でどこかで保持して
それをdeleteするなどのケースも大丈夫なのか気になりました。


To:tetrapod様

>n the second alternative (delete array) if the dynamic type of the object to be deleted
differs from its static type, the behavior is undefined.

これは「動的な型を静的な間違った型にキャストしてdeleteすると
未定義な動作になる」
ということでしょうか。
これには、今回のようなchar*でnewしたものをunsigned char*にキャストして
deleteしたり、
またはsigned int* でnewしたものをunsigned int*にキャストしてdelete
したりするケースはどうなるのでしょうか?
同じsignedやunsignedはあくまで符号の扱いで、型としては静的に同じだから
問題ないのでしょうか。

お二方ご回答ありがとうございました。

編集 削除
tetrapod  2008-05-09 08:42:44  No: 68260  IP: 192.*.*.*

> 同じsignedやunsignedはあくまで符号の扱いで、型としては静的に同じだから
規格書はそんなことは述べていないので「異なる型」である (7.1.5.2)

静的な型とはソースコード上での当該式の型(正確には指し示す先の型)
 (最初の例では uc が示す先の型 unsigned char )
動的な型とはそのポインタ式が指し示す先の実体の型
 (最初の例では new[] したところの char )
配列の delete[] の際にこの両者が異なるとダメだよと規格書は言っている
# クラス型限定とかそういう文言は無いので POD でもダメ

というわけで、提示コードはまさに規格書がダメと言っている例に該当していることになる

ただし
ある特定コンパイラ+特定オプション指定下で問題が発生するかしないかと
言語規格書として「絶対に問題なく動作する」状況を明示するのとでは
話の次元が異なるわけだ

移植性とか互換性とか拡張性とかを無視して自己責任で違う型で delete[] したければ
どうぞご勝手に。俺はやらない

# デバッグツールが new や delete を差し替えてログ取るような状況下で
# 誤動作とかメモリリークレポートとかあがってきても不思議ぢゃない

編集 削除
rin  2008-05-09 16:06:31  No: 68261  IP: 192.*.*.*

なにゆえに
newするときの型と、deleteするときの型を変えたいのか
その目的が知りたい

編集 削除
Bdasj  2008-05-10 20:23:05  No: 68262  IP: 192.*.*.*

tetrapodさんありがとうございます。
理解できました。

rinさんへ。
目的というか、やったらどうなるのかなというのが知りたかったのです。

編集 削除