トップ回答者
配列の再拡張の仕方をお教え下さい。

質問
-
Code Snippet
case EMR_LINETO:
//Polylineの終点を取得
x = lpEMFR->dParm[0];
y = lpEMFR->dParm[1];//出力用(兼WMF変換用)のEMFにPolyline命令を書き込み
EMRPOLYLINE empoly;
empoly.emr.iType = EMR_POLYLINE; //レコードの種類にPolylineを指定
empoly.emr.nSize = sizeof(EMRPOLYLINE) + sizeof(POINTL); //レコードのサイズ(バイト単位)
empoly.rclBounds.left = rl; //Polyline描画の基準になる四角形の左端値(デバイス単位)
empoly.rclBounds.top = rt; //上端値(デバイス単位)
empoly.rclBounds.right = rr; //右端値(デバイス単位)
empoly.rclBounds.bottom = rb; //下端値(デバイス単位)
empoly.cptl = 2; //Polyline描画に用いる点の数
empoly.aptl[0].x=ox; //1点目x座標
empoly.aptl[0].y=oy; // y座標
empoly.aptl[1].x=x; //2点目x座標
empoly.aptl[1].y=y; // y座標
PlayEnhMetaFileRecord(hDC , lpHTable , (ENHMETARECORD FAR *)&empoly , nObj);
//Polylineの始点を取得
ox = x;
oy = y;
return TRUE;
break;上記のようにして,EMFのLineToレコードをPolylineレコードに置き換えるコードを書いていたのですが,
VC++6.0ではこれで動いていたのに,VC++2008で動かしてみたら,このempolyを使用しているスコープが終了するときにスタックエラーが発生します。
Code Snippetempoly.aptl[1].x=x; //2点目x座標
empoly.aptl[1].y=y; // y座標をコメントアウトするとエラーは発生しません。
EMRPOLYLINE 構造体の定義が
Code Snippettypedef struct tagEMRPOLYLINE
{
EMR emr;
RECTL rclBounds; // Inclusive-inclusive bounds in device units
DWORD cptl;
POINTL aptl[1];
}となっていたので,むしろ今までちゃんと動いていた方が不思議なような気もします。
VC++2008でちゃんと動かすには一番上のコードの中でaptlの配列を[2]に再拡張しなくてはならないと思うのですが,
具体的にどうコーディングしたらよいのか,色々検索して試行錯誤しても分かりません。
非常に初心者的質問で申し訳ないのですが,お答えいただけると非常に助かります。宜しくお願いします。
回答
-
monger_monger さんからの引用 お騒がせいたしました。静的配列を使う場合は,2行目のbufの前に"&"を追加すればOKでした。 PEMRPOLYLINE pEmpoly = reinterpret_cast<PEMRPOLYLINE>(&buf);
ということでしょうか?
これで OK になるというのは、それはそれでヘンだと思います。
monger_monger さんからの引用 newとdeleteの使い方はこれで大丈夫でしょうか。何かおかしな所があったらご指摘願います。 途中で例外が発生しなければ大丈夫です。
逆に言えば、new と delete の間で例外が発生してしまうと buf が開放されませんので注意が必要です。
例えば以下のように vector を使えば、途中の例外についてあれこれ悩まなくてもよくなります。
vector<BYTE> buf(sizeof(EMRPOLYLINE) + sizeof(POINTL));
PEMRPOLYLINE pEmpoly = reinterpret_cast<PEMRPOLYLINE>(&buf[0]);
この方法だと、buf の中身は最初からゼロクリアされているというオマケつきです。
すべての返信
-
zakioさま,お返事有り難うございます。初歩的なことで申し訳ないのですが,更に質問させて下さい。
Code SnippetBYTE buf[sizeof(EMRPOLYLINE) + sizeof(POINTL)];
PEMRPOLYLINE pEmpoly = reinterpret_cast<PEMRPOLYLINE>(buf);pEmpoly->emr.iType = EMR_POLYLINE; //レコードの種類にPolylineを指定
とした場合,3行目で「保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます」というエラーが出てしまいます。
1時間ちょっとあれこれしてみましたが,解決できませんでした。
ダイアログベースでexeを作ってその中(OnOK)で上記コードを実行しても同様のエラーでした。
解決策をご教示いただければ幸いです。
-
お騒がせいたしました。静的配列を使う場合は,2行目のbufの前に"&"を追加すればOKでした。
また,下記コードのように,動的配列を利用することでも意図する結果が得られました。
Code Snippet{
BYTE *buf = new BYTE[sizeof(EMRPOLYLINE) + sizeof(POINTL)];
memset(buf,0,sizeof(EMRPOLYLINE) + sizeof(POINTL));
PEMRPOLYLINE pEmpoly = reinterpret_cast<PEMRPOLYLINE>(buf);pEmpoly->emr.iType = EMR_POLYLINE; //レコードの種類にPolylineを指定
pEmpoly->emr.nSize = sizeof(EMRPOLYLINE) + sizeof(POINTL); //レコードのサイズ(バイト単位)
pEmpoly->rclBounds.left = rl; //Polyline描画の基準になる四角形の左端値(デバイス単位)
pEmpoly->rclBounds.top = rt; //上端値(デバイス単位)
pEmpoly->rclBounds.right = rr; //右端値(デバイス単位)
pEmpoly->rclBounds.bottom = rb; //下端値(デバイス単位)
pEmpoly->cptl = 2; //Polyline描画に用いる点の数
pEmpoly->aptl[0].x=ox; //1点目x座標
pEmpoly->aptl[0].y=oy; // y座標
pEmpoly->aptl[1].x=x; //2点目x座標
pEmpoly->aptl[1].y=y; // y座標
PlayEnhMetaFileRecord(hDC , lpHTable , (ENHMETARECORD FAR *)pEmpoly , nObj);delete[] buf;
}newとdeleteの使い方はこれで大丈夫でしょうか。何かおかしな所があったらご指摘願います。
-
monger_monger さんからの引用 zakioさま,お返事有り難うございます。初歩的なことで申し訳ないのですが,更に質問させて下さい。 Code SnippetBYTE buf[sizeof(EMRPOLYLINE) + sizeof(POINTL)];
PEMRPOLYLINE pEmpoly = reinterpret_cast<PEMRPOLYLINE>(buf);pEmpoly->emr.iType = EMR_POLYLINE; //レコードの種類にPolylineを指定
とした場合,3行目で「保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます」というエラーが出てしまいます。
1時間ちょっとあれこれしてみましたが,解決できませんでした。
ダイアログベースでexeを作ってその中(OnOK)で上記コードを実行しても同様のエラーでした。
解決策をご教示いただければ幸いです。
アラインメントのことをすっかり忘れてました。
細切れの BYTE を無理やり DWORD にする際に、気まずい境界をまたいでしまったのかも知れません。
ヘンな例を挙げて時間を無駄にさせてしまい、申し訳ありません。
# サイズの指定も間違ってましたので、こちらは投稿の方を編集しておきました。
-
monger_monger さんからの引用 お騒がせいたしました。静的配列を使う場合は,2行目のbufの前に"&"を追加すればOKでした。 PEMRPOLYLINE pEmpoly = reinterpret_cast<PEMRPOLYLINE>(&buf);
ということでしょうか?
これで OK になるというのは、それはそれでヘンだと思います。
monger_monger さんからの引用 newとdeleteの使い方はこれで大丈夫でしょうか。何かおかしな所があったらご指摘願います。 途中で例外が発生しなければ大丈夫です。
逆に言えば、new と delete の間で例外が発生してしまうと buf が開放されませんので注意が必要です。
例えば以下のように vector を使えば、途中の例外についてあれこれ悩まなくてもよくなります。
vector<BYTE> buf(sizeof(EMRPOLYLINE) + sizeof(POINTL));
PEMRPOLYLINE pEmpoly = reinterpret_cast<PEMRPOLYLINE>(&buf[0]);
この方法だと、buf の中身は最初からゼロクリアされているというオマケつきです。
-
-
zakioさま,返信ありがとうございます。
PEMRPOLYLINE pEmpoly = reinterpret_cast<PEMRPOLYLINE>(&buf);
でOKだったのですが,念のため
PEMRPOLYLINE pEmpoly = reinterpret_cast<PEMRPOLYLINE>(&buf[0]);
に修正しました。こちらでも正しく動作します。
#include<vector>//--------------------------------------------------
using namespace std;
vector<BYTE> buf(sizeof(EMRPOLYLINE) + sizeof(POINTL));
PEMRPOLYLINE pEmpoly = reinterpret_cast<PEMRPOLYLINE>(&buf[0]);
こちらも試してみました(青字の追加が必要でした)が,正しく動作しているようです。
ご助言ありがとうございました。
-
Azulean さんからの引用 BYTE buf[sizeof(aa) + sizeof(bb)]が細切れ(のBYTE配列)になるんでしょうか?(この一文において、aaとbbはそれぞれ何らかの型とします)
sizeof演算子の結果及びこの足し算自体はコンパイル時点で定数になり、連続した配列になると考えました。ちょっと表現が悪かったです。
「細切れ」というよりは「BYTE の寄せ集め」といったところでしょうか。
おっしゃるとおり、buf はメモリ上では連続しています。ただ、buf は単に連続しているだけで、先頭のアドレスが(例えば4の倍数というような)キリのいい場所にあることは保証されなかったはずです。(逆に new や malloc で動的に確保した領域は何らかの保証があったはず。)
さらに、環境によっては int や double のような多バイトのデータがキリのよいアドレスに配置されていないと憤死してしまう恐れがあります。例えば、buf が運よく4の倍数アドレスに配置されたなら、
0000: buf[0] <--- ここから
0001: buf[1]
0002: buf[2]
0003: buf[3] <--- ここまでを int として扱う
-----------
0004: buf[4]
:それを無理やり int (4byte とする) と見なしても大丈夫なのですが、以下のように buf の位置がズレると int が気まずい場所に配置されてしまうことになります。
0001: buf[0] <--- ここから
0002: buf[1]
0003: buf[2]
-----------
0004: buf[3] <--- ここまでを int として扱う
0005: buf[4]
:ですから、buf だと失敗するのに &buf や &buf[0] でセーフだった事が少々気にかかっています。
& が付いたことでコンパイル時に何らかの調整が入ったということなのでしょうか?
それともコンパイルオプション等に依存するのでしょうか?
残念ながら手元の環境(Core2, XP, VS2005)では再現しませんでした。 -
zakio さんからの引用 さらに、環境によっては int や double のような多バイトのデータがキリのよいアドレスに配置されていないと憤死してしまう恐れがあります。
「環境による」という範囲が広く普及しているPCで起こるのでしょうか?(もしくは、ごく限られた環境?)
情報源をご提示頂けると助かります。
私見:
広く起こりえるとすると、pack(1)とされている範囲で宣言される構造体は非常に危険ですし、また、newやmallocで確保したBYTE配列の1番目から、例えば&buf[1]のようなところから構造体を割り当てた場合に動作がおかしくなります。
容易に起こりえる状況だと思いますが、広く危険視されていないように感じます。
-
Azulean さんからの引用 「環境による」という範囲が広く普及しているPCで起こるのでしょうか?(もしくは、ごく限られた環境?) 私の理解は、
・x86 なら大丈夫 (読み書きの時間に影響する)
・他のアーキテクチャは要注意 (x86 の方が少数派)といった程度です。(誰かフォローをお願いします)
# ちょっと前に W-ZERO3 のソフトを書いた時にはアラインメントの事を気にしたような気がするので、ARM は多分駄目です。世の中で広く普及しているのは x86 アーキテクチャの PC だと思いますので、アラインメントに気をつけなければならない環境というのは少数派ということになるのでしょうか?
でも Windows CE とか Mobile の世界になると、とたんに x86 以外の CPU が登場してきますので、普段から気をつけておくに越したことはないと思います。# XP や Vista って x86 以外のアーキテクチャに対応してましたっけ?
-
ARM のマニュアルを斜め読みしたところでは、アラインメントのエラーチェックはフラグによって制御できるようなつくりになっているようです。
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0100i/index.html
> [Non word-aligned addresses]
> For CP15_reg1_Ubit == 0 the store coprocessor register instructions ignore the least
> significant two bits of address. For CP15_reg1_Ubit == 1, all non-word aligned
> accesses cause an alignment fault.
それから、「アラインメント」をキーワードに検索をかけると結構引っかかりました。
皆さん色々苦労されているようです。 -
動的に確保した領域のアドレスは ISO/IEC 14882:2003 で以下のように保証されていました。
3.7.3.1 Allocation functions [basic.stc.dynamic.allocation] - 2 -
... The pointer returned shall be suitably aligned so that it can be converted to a
pointer of any complete object type and then used to access the object or array
in the storage allocated ...
返ってきたポインタはうまい具合に位置合わせされてるから、
どんなオブジェクトのポインタに変換しても大丈夫だよ (超簡訳 by zakio)
-
zakio さんからの引用 ・x86 なら大丈夫 (読み書きの時間に影響する)
・他のアーキテクチャは要注意 (x86 の方が少数派)なるほど。
Mobile系では起こりうると言うことで理解しました。
情報ありがとうございます。
zakio さんからの引用 世の中で広く普及しているのは x86 アーキテクチャの PC だと思いますので、アラインメントに気をつけなければならない環境というのは少数派ということになるのでしょうか?
でも Windows CE とか Mobile の世界になると、とたんに x86 以外の CPU が登場してきますので、普段から気をつけておくに越したことはないと思います。何でもかんでもBYTEの配列に詰めるなとは言えますね。
ただ、普段から気をつけてコードを書くと言うよりは、そういう環境もあり得るという認識だけ持っておくようなことが多くなるのかなと考えています。PCアプリしか組まないような会社もあるわけですし。
もちろん、プラットフォーム間で行き来するようなコードを書く人は注意が必要になることには理解しています。
zakio さんからの引用 # XP や Vista って x86 以外のアーキテクチャに対応してましたっけ?
x64は似たり寄ったりといえばそうですけれども、一応挙げてみますか。
-
Azulean さんからの引用 ただ、普段から気をつけてコードを書くと言うよりは、そういう環境もあり得るという認識だけ持っておくようなことが多くなるのかなと考えています。PCアプリしか組まないような会社もあるわけですし。 そうですね。私ならコメントを入れる程度で済ませてしまうかも知れません。
ただ、「危ないのは分かってる。マネすんな。」と書いておくだけでもずいぶん違うのではないでしょうか。余裕があれば、double の配列を使うという手もありますね。
サイズ計算が面倒ですが、テンプレート化してしまえば「お気楽静的バッファ」として便利に使えます。あと、double のアラインじゃ満足できない(例えば SIMD 用バッファを確保したいような)場合は、
実行時に演算が入ってしまいますが、以下のようなラッパークラスをかましてやればうまくいくと思います。Code Snippettemplate <std::size_t BUFFER_SIZE, std::size_t ALIGN_SIZE = sizeof(double)>
class AlignedBuffer {
char buf_[BUFFER_SIZE + ALIGN_SIZE - 1];
public:
void* GetPtr() {
const std::size_t offset = (ALIGN_SIZE - (reinterpret_cast<std::size_t>(buf_) % ALIGN_SIZE)) % ALIGN_SIZE;
return buf_ + offset;
}
};これを、こんな風に使えば、任意のアラインメントを実現できるはずです。
使用例AlignedBuffer<sizeof(Hoge)> buf1;
Hoge* pHoge = static_cast<Hoge*>(buf1.GetPtr());AlignedBuffer<sizeof(double) * 10, 16 /* must be aligned 16 byte boundaries */> buf2;
double* pArray = static_cast<double*>(buf2.GetPtr());ただし、クラスの場合は placement new で初期化する必要がありますので、このやり方は POD での使用にとどめておいた方が無難です。