• ベストアンサー

アラインメントに厳格なCPU対策

アラインメントについてそろそろ詳しく知りたくなりました。 C++です。 1.以下のalignof(type)でアラインメントは求まることが保証されますか? template<class T> class alignmentof__{ struct S { char c; T t; }; public: enum { value = offsetof(S, t) }; }; #define alignof(type) (alignmentof__<type>::value) 2.また以下のコードで #define SYSTEM_ALIGN 8 template<class T> struct A_l_i_{ enum { Small = alignof(T) <= SYSTEM_ALIGN }; }; template<class T, bool B> struct Align{ template <class U> static T* address(U* p){ return (T*)(UINT(p+alignof(T)-1)&~(alignof(T)-1)); } }; template<class T> struct Align<T,true>{ template <class U> static T* address(U*p){ return (T*)p; } }; #define ALISS(C,P) (Align<C,A_l_i_<C>::Small>::address(P)) これで、alignof(T)が8以下なら下の特殊化がなされ、8より大きければ上の通常のAlignが展開されます。 そしてaddress関数には別に適当に確保して、アラインメントが class T 用に調整されていないバッファの先頭アドレスを渡し、調整後のアドレスを返却する、という意図があるのですが これは本当にちゃんとそういう動作になっているのでしょうか? 3.やってみたら私の環境ではデストラクタが10回呼ばれ、正常に処理を抜けることができたのですが 上記を使い以下のように配置newを用いることは本当に可能ですか? (保証されているのでしょうか?) class BBB { double d[8]; public: ~BBB(){static int i=0; if (++i==10) printf("デストラクタが10回呼ばれました"); } }; char c[ sizeof(BBB)*10+alignof(BBB)-SYSTEM_ALIGN ]; BBB* b = new( ALISS(BBB,c) ) BBB[10]; for (int i=10;i--;) { printf( "%d\n",int(b+i)-(int)c ); b[i].~BBB(); } (配置newを使って配列確保し、個々に対してデストラクタを呼んでいる、つもりなのですが、これは正しいのでしょうか?) 4.そもそも、配置newでない、また再定義していないデフォルトのnewはアラインメント的に大丈夫なことが保証されてて、したがって通常はこんな面倒なことをしなくても問題ない、のでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

配列new ですが, ちょっと規格から引っ張ってみますと ・new T[5] は operator new[](sizeof(T)*5+x) を呼び出す ・new(2, f) T[5] は operator new[](sizeof(T)*5+y, 2, f) を呼び出す とあります. この x や y が #2 の最初で気にした値ですが, この値について規格では ・非負である (当然) ・(配列) new の呼び出しごとに違ってもよい としています. 従って, 処理系を決めない限りこの値を事前に決め打ちすることは不可能です (もっといえば, 処理系が決まっていたとしても「事前に」決まるとは限らない). そして, 配置new がどのように動作するかにもよりますが, 第1引数の値 (上の例だと sizeof(T)*5+y) が確保した大きさを超えている場合にはおかしなことになるかもしれません. でもって, 今の C++ では「ポインタと行って帰ってできる整数型」は定義されていません. ほとんどの場合 size_t でいいと思いますが, 規格上の保証はないような気がする. あ... じっと規格を見ていたら 1 がまずいことに気づいてしまった.... 今の例では BBB がデストラクタを持っているので POD ではなく, してがってそのオブジェクトをメンバに持つ alignmentof_<BBB>::S も POD ではありません. そして, offsetof は「POD な構造体または共用体にしか使えない」と (今の規格には) 書いてあるのでアウト.... ついでにいうと Align<T, B>::address も微妙に怪しい. 「アラインメントが 2のべき」じゃないシステム (そんなのあるのか?) に遭遇するとダメだ.

LongSecret
質問者

お礼

>でもって, 今の C++ では「ポインタと行って帰ってできる整数型」は定義されていません. ほとんどの場合 size_t でいいと思いますが, 規格上の保証はないような気がする. では 後でもしものことが(ないとは思いますが)あった場合に、 typedef size_t 新しい型名; というのを自分で定義しておけば、僅かな手間で直せるので、念には念のため、ならば、現実的にはこうしておくことにしましょう。 > 「アラインメントが 2のべき」じゃないシステム かつ、アラインメントに寛容ではない、ですね? しかし、ありえたとして現実的には現状から普及できるでしょうか… もしあった場合も考えるなら 単に式を変えるか コンパイル時に2のべきにならない場合はコンパイルエラーを出すようにテンプレートをもう少し改造しつつ、対象を限定する 等の方法で対処することにしましょう。 問題はその他の2点です。 boostにそっくり同じ目的のものがあることを知ったのですが boost::alignment_of<T>::value は、非POD(日本語版の規格書的に書くと非C互換型?)対応なのでしょうか? また、対応しているとすれば、どこら辺が決め手になっているのでしょうか?(#defineが多すぎて把握するが難しいです) >・new T[5] は operator new[](sizeof(T)*5+x) を呼び出す >・new(2, f) T[5] は operator new[](sizeof(T)*5+y, 2, f) を呼び出す 及び、規格では未定 というのは確認できましたが、それでは 配列newに関しては 配置newとして使わない、以外に どのように対処することができるでしょうか? あともうちょい別件ですが char c[sizeof(BBB)]; BBB* b=new(c) BBB; はまずいけど char* c=new char[sizeof(BBB)]; BBB* b=new(c) BBB; はOKと読めなくもない感じがするのですが 実際にこの解釈でいいのでしょうか?

LongSecret
質問者

補足

ちょっと意味が伝わりにくいかもしれないので --------- 配置newとして使わない、以外に どのように対処することができるでしょうか? ↓ 配置newとして使いたい場合は どのようにすれば対処することができるでしょうか? または、配置newとして使わない、以外には規格上完全な保証を得る方法はないのでしょうか?

その他の回答 (3)

  • BLK314
  • ベストアンサー率55% (84/152)
回答No.3

C/C++ではポインタのサイズについて定めていません。 64ビットを視野に入れると longとポインタの互換性があるとは言い切れません。 http://ja.wikipedia.org/wiki/64%E3%83%93%E3%83%83%E3%83%88 LLP64(Win64等)ではlongは32ビットに対し、ポインタは64ビットなので アドレスをlongに代入することは出来ません。 (UINTも同じ) 強いて言えば、size_tが64ビットです。(64ビットコンパイル時) 32ビットとの互換性を持たせたいときはsize_tを使います。 http://d.hatena.ne.jp/Schima/20091101/1257052506

LongSecret
質問者

お礼

>LLP64(Win64等)ではlongは32ビット 確かにそう書いてありますねw ありがとうございます♪ 現実解としてはsize_tあたりになるということですね?

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

あっと, そういえば ・配列を確保する場合には, 「オブジェクトに必要な分」より多いメモリが必要かもしれない (つまり A[5] を確保するためのメモリは 5*sizeof(A) より多いかもしれない) ・C++ には UINT なんて型は存在しないし, unsigned int のことだとしてもこれがポインタと互換であるとは限らない という問題があるんだった.

LongSecret
質問者

お礼

ありがとうございます。 前者については上記コードからSYSTEM_ALIGNを取り払うとして char c[sizeof(BBB)*10+alignof(BBB)-1]; で埋まらないかもしれない、オブジェクトの配列確保という意味での別件ということでしょうか?(sizeofはアラインメント済みの数値のはずなので単体確保10回ならおそらくこの式で問題ないと思うのですが…) それのサイズを知る方法はありますか? 後者については UINTはWinDef.h インクルードしてるという前提でしたw つまり確かにunsigned intのことですが、ポインタと必ず互換性があるのはlong型でしたでしょうか?

LongSecret
質問者

補足

(約2年後、この補足が最後に書かれました) まぁとりあえず、今現在まで困ったこともなかった(配置newを配列で使わなくても)ので この質問は解決として置きますね。 どうもありがとうございました♪

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

operator new が呼び出すメモリ割り当て関数については規格 (5.3.4 節のパラグラフ 10) に Because allocation functions are assumed to return pointers to storage that is appropriately aligned for objects of any type とあるので, デフォルトの operator new ではアラインメントについて考える必要はない (はず) です.

LongSecret
質問者

お礼

ありがとうございます♪ > デフォルトの operator new ではアラインメントについて考える必要はない (はず) です. 安心しました。 ちょうどTacosanさんにお聞きしたかったのですが、規格書を調べるときに効率的な方法ってありますか? 今のところ英語版の購入はしていないのですが、少なくとも日本語版のPDFをネット上で見たとき、検索機能があんまりうまくないというか、基本的な調べ方がわからないので、全部見てると時間がかかってしまいます。

LongSecret
質問者

補足

ん、まてよ char のアラインメントは1だから #define SYSTEM_ALIGN 8 なんてものを取っ払って template<class T, class U> struct A_l_i_{ enum { Small = alignof(T) <= alignof(U) }; }; 及び #define ALISS(T,U,P) (Aligne<T,A_l_i_<T,U>::Small>::address(P)) としておいて BBB* bb = new( ALISS(BBB,char,ccc) ) BBB[10]; とするか #define ALISS(T,U,P) (Aligne<T,A_l_i_<T,U>::Small>::address(P)) #define ALISS1(T,P) (Aligne<T,A_l_i_<T,char>::Small>::address(P)) としといて BBB* bb = new( ALISS1(BBB,ccc) ) BBB[10]; にしておかないとまずいですかね。

関連するQ&A

  • new演算子で困っています。

    基底クラスclass1と派生クラスclass2でnewを使って同じサイズの2次元配列pとqを作ったのですが、メモリ内の同じ場所を参照してるみたいで、class2の配列qで配列の中身を書き換えたら、class1の配列pの中身も書き換えられているんですが、対処法があれば教えてください。 class class1{ protected:      int** p; public:     class1(int n)     {   int i;        *p=new int[n];        for(i=0;i<n;i++){          p[i]=new int[2];        }      }    :    : }; class class2 : public class1{     int** q; public:     class2(int n)     {   int i;        *q=new int[n];        for(i=0;i<n;i++){         q[i]=new int[2];        } }    :    : };

  • classのdelete

    C++についてです。 class aaa; class bbb; とあったとします。 class aaaは、内部にclass bbb型の配列を 持っているとすると、aaaをdeleteする場合には aaaのデストラクタがcallされると思うのですが bbbのデストラクタは自動でcallされるのでしょうか (class bbbは、明示的にdeleteしないといけないのでしょうか?) 宜しく御願い致します。

  • c++11での文字列リテラルの特殊化について

    c++11言語でのテンプレート部分特殊化についての質問です。 コメントアウト部分は出力結果です template<class T> struct VT { static const int type = 1;}; template<class T,int N> struct VT< T[N] > { static const int type = 2;}; template<class T,int N> struct VT< const T[N] > { static const int type = 3;}; template<class T> struct VT< T* > { static const int type = 4;}; template<class T> struct VT< const T*const > { static const int type = 5;}; #include<iostream> #include<typeinfo> int main(){ std::cout<<"A:"<< VT< char >::type << std::endl; // A:1 std::cout<<"B:"<< VT< char[10] >::type << std::endl; // B:2 std::cout<<"C:"<< VT< char* >::type << std::endl; // C:4 std::cout<<"D:"<< VT< char const [1] >::type << std::endl; // D:3 std::cout<<"E:"<< VT< decltype("") >::type << std::endl; // E:1 std::cout<<"G:"<< typeid( char const [1] ).name() << std::endl;// G:char const [1] std::cout<<"H:"<< typeid( "" ).name() << std::endl;// H:char const [1] } 型名を直接記述したD,G、文字列リテラルを記述したE,H。 コンパイラ毎の差はあれど、GとHの型名は同じものが表示されます。 ですが、[D:3] [E:1]と値は違い、別の特殊化テンプレートが使われています。 この部分が分かりません。 また、配列リテラル、文字列リテラルに対し部分特殊化テンプレートを宣言する方法などありましたら、ご教示お願いします。

  • 線形リスト

    ファイルから単語を読み込んでいき、単語とその単語の数を出力するプログラムです。単語の区切り方は英文字以外の時です。 例 http://www.sig.sig.org だったとしたら http 1回 www 1回 sig 2回 org 1回 という課題なのですが、while文で読み込んで英文字以外が入力されるたびにif文でぬけ、単語の登録、比較するプログラムを作ったのですが、まったく動きません。(たぶん関数への引渡しとかがおかしいのだと思うのですが) どこがどうちがうのか教えてくださいお願いします。 #include<stdio.h> struct node{//構造体 int count; char tango[20]; struct node *next; }; struct node *new(char t[20]) {//単語の入力 struct node *p; strcpy(p->tango,t); p->count=0; return p; } struct node *in(struct node *p,char t[20]) {//単語の比較 int c; c=strcmp(p->tango,t); if(c==0){ p->count++; return p; } else if(p->next==NULL) {p->next=new(t);} else {in(p->next,t);} } int *pt(struct node *p){//出力 if(p==NULL)return; else printf("%s:%d\n",p->tango,p->count); pt(p->next); } int main() { FILE *fp; int b,i; char a[20],ch; struct node *root; i=0;b=0; for(i=0;i<20;i++) a[i]=0;//初期化 i=0; fp=fopen("data1.txt","r"); if(fp==NULL)//ファイルを開く return; while((ch=fgetc(fp)) != EOF){//文字の読み込み if(!((97<=ch)&&(ch<=122)&&(65<=ch)&&(ch<=90))){//英字以外の時 b=b+1; if(b==0)//初期のみ root=new(a); else//それ以降 in(root,a); for(i=0;i<20;i++){//初期化 a[i]=0; i=0; } } else{//英文字の時 a[i]=ch; i=i+1; } } pt(root);//出力関数へ fclose(fp); }

  • デストラクタについて

    #include <iostream> #include <string> using namespace std; #define NUM 2 //登録人数 class Person{ char *name; int *age; char *hobby; public: Person() { name = new char; age = new int; hobby= new char; } void Set(char *n,int a,char *h) { name=n; *age=a; hobby=h; } char *Get_name(void) { return name; } int Get_age(void) { return *age; } char *Get_hobby(void) { return hobby; } ~Person() { cout<<name<<"のデストラクタ\n"; delete [] name; delete age; delete [] hobby; } }; int main(void) { Person *p; int i; p=new Person[NUM]; p[0].Set("永嶋",21,"映画鑑賞"); p[1].Set("平林",54,"車"); for(i=0;i<NUM;i++){ cout<<"\n名前:"<<p[i].Get_name(); cout<<"\n年齢:"<<p[i].Get_age(); cout<<"\n趣味:"<<p[i].Get_hobby()<<"\n"; } delete [] p; return 0; } というプログラムを作成したのですが デストラクタの3つのdeleteがおかしいようなのですが どのような部分が問題となっているのでしょうか? 回答・アドバイス宜しくお願いいたします。

  • C++の話です。

    C++の話です。 静的メンバ変数としてクラスを宣言した場合、デストラクタが呼ばれていないようなのですが、呼ぶ方法はありませんか? できれば「new」「delete」を使わずできると理想的です。 分かる方教えていただけると助かります。 以下、サンプルコードです。 「デストラクタが呼ばれました」と出力されない上、デバッガを使って試してみましたが、やはり呼ばれていないようです。 #include<iostream> class Test{   public:     ~Test(){       std::cout<<"デストラクタが呼ばれました"<<std::endl;     } }; class A{   private:     static Test T; }; int main(){   A a;   return 0; }

  • メンバ関数ポインタを値とするコンテナの定義方法

    こんにちは、boundaryといいます。 あるクラスのメンバ関数のポインタをmapで管理したいので すが、定義でエラーになってしまいます。 class CFunc { private: bool FuncA(); bool FuncB(); }; このようなクラスに対して、 template <class T> class FuncMap { private: typedef bool (T::*Func)(); typedef map<int, T::Func> _FuncMap; _FuncMap FMap; public: void set(int, T::Func); T::Func search(int); }; を用意しました。 template <class T> void FuncMap<T>::set(int i, T::Func pFunc) { FMap.insert(pair<int , T::Func>(i ,pFunc)); } template <class T> T::Func FuncMap<T>::search(int i) { map<T::Func, int>::iterator FuncMapIte; FuncMapIte = FMap.find(i); return T::Func; } と書いたのですが、 error C2244: 'FuncMap<T>::set' : 関数のオーバーロードが解決できません。 error C2954: テンプレートの定義はネストできません。 error C2244: 'FuncMap<T>::search' : 関数のオーバーロードが解決できません。 との事です。 色々試行錯誤したのですが、うまくいきません。 定義のどこがいけないのでしょうか? よろしくお願いします。 VC6.0SP5 WINDOWS2000です。

  • 試作クラス使用C++プログラムが動かない原因

    試しに作成した以下のプログラムにビルドエラーが発生し,困っているので質問しました. コンパイラは,「BBB *b」などクラスポインタ変数の部分がエラーと指摘しているのですが(他にも数か所ありますが…),間違っている理由が分からず困ってます. ご回答,よろしくお願い致します. ----------------------------------- #include <iostream> using namespace std; class AAA { public: // コンストラクタ AAA(){ b = new BBB( this ); } // デストラクタ ~AAA(){ delete b; } // メンバ変数 BBB *b; int i; // メンバ関数 void displayB(){ b->displayA(); } }; class BBB { public: // コンストラクタ BBB( AAA *a ){ this->a = a; } // デストラクタ ~BBB(){} // メンバ変数 AAA *a; int j; // メンバ関数 void displayA(){ printf( "%d\n", a->i ); } }; int main() { // 変数の定義 AAA a; // 変数の初期化 a.i = 2; a.b->j = 5; // 出力 printf( "%d\n", a.i ); a.b->displayA(); return 0; } -----------------------------------

  • C言語で型汎用性のある関数を作成したい

    C言語です。 下記のコードでは、期待した通りにメンバにアクセスできるのですが、 構造体のメンバが多くなったとき、構造体をネストしているときには、 挙動がおかしく(なにか別の値をとりだしてしまっている?)なります。 どのようにしたら型汎用のある関数を書くことができるでしょうか? (C++ならば、汎用(テンプレート)関数があるけれども。。。) #include <stdio.h> struct hanyo{ int i; }; struct aaa{ int i; long l; char str[100]; }; struct bbb{ int i; }; void hoge(hanyo *ph){ printf("%d\n", ph->i); return; } int main(){ aaa a; a.i = 1; bbb b; b.i = 2; hoge((hanyo *)&a); hoge((hanyo *)&b); return 0; } ----実行結果---- 1 2 続行するには何かキーを押してください . . .

  • 構造体を使ったプログラム

    学校でC言語を勉強しています。(まだ初心者です) テストの成績を入力して、その結果を降順にソートしたいんですけど、 下記のプログラムでは、正常に動かないです。 struct seiseki { char nama; int sansuu; int rika; int goukei; }; ~~~~~~~~~~~~~~~~~~~~ struct seiseki class_a; struct seiseki class_b; struct seiseki *ptr1; struct seiseki *ptr2; ptr1 = class_a; ptr2 = class_b; ~~~~成績はあらかじめ入力済み~~~~ sout(class_a, 3); sout(class_b, 3); void sout(struct seiseki *p, int num) { struct seiseki temp; int count; int j; for (count = 1; num > count; count++) { temp = p[count]; for (j = count; j > 0 && p[j - 1].goukei < temp.goukei; j--) { p[j] = p[j - 1]; } p[j] = temp; p++; } } class_aだけを実行するプログラムだとちゃんと表示されますが、 一度でclass_aとclass_bを実行するプログラムだと表示がおかしくなります。 どなたか教えてください。

専門家に質問してみよう