大きなサイズの配列とEXC_BAD_ACCESSについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
cotora
記事: 4
登録日時: 8年前

大きなサイズの配列とEXC_BAD_ACCESSについて

#1

投稿記事 by cotora » 8年前

C++初心者です。周囲に質問できる人がいないため、こちらで相談させてください。

大きなサイズのArrayの作成に関して、疑問があります。
例えば、以下のコードは問題なく動作しています。

コード:

int main(){
  char c1[1000000][12];
}
ところが以下のようにc1をクラスメンバとして宣言しコンパイルすると、EXC_BAD_ACCESSとなってしまいます。

コード:

class Cls{
private:
  char c[1000000][12];
public:
  Cls(){};
  ~Cls(){};
};

int main(){
  Cls d;
}
ここで、配列のサイズを小さく(たとえば c[1000000][3])するとEXC_BAD_ACCESSが出なくなります。
この原因は一体なぜでしょうか。また、今回のようにクラスメンバに大きなサイズの配列を用いたい場合はどのようにするのが良いのでしょうか。
(開発環境にはMac(Xcode)を使用しています。)

どうぞよろしくお願いいたします。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 大きなサイズの配列とEXC_BAD_ACCESSについて

#2

投稿記事 by みけCAT » 8年前

cotora さんが書きました:この原因は一体なぜでしょうか。
「大きな配列を静的でないローカル変数として確保した結果、アクセス違反」というパターンなので、おそらくスタックオーバーフローでしょう。
Compiler Explorerのx86-64 gcc 4.8.2、コンパイルオプションなしで変換したところ、
最初のコードは

コード:

main:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $11999880, %rsp
        movl    $0, %eax
        leave
        ret
2番目のコードは

コード:

Cls::Cls():
        pushq   %rbp
        movq    %rsp, %rbp
        movq    %rdi, -8(%rbp)
        popq    %rbp
        ret
Cls::~Cls():
        pushq   %rbp
        movq    %rsp, %rbp
        movq    %rdi, -8(%rbp)
        popq    %rbp
        ret
main:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $12000000, %rsp
        leaq    -12000000(%rbp), %rax
        movq    %rax, %rdi
        call    Cls::Cls()
        leaq    -12000000(%rbp), %rax
        movq    %rax, %rdi
        call    Cls::~Cls()
        movl    $0, %eax
        leave
        ret
となりました。
最初のコードではスタックポインタを減算しただけで指された(おそらく範囲外の)領域を使用していないのでアクセス違反は発生せず、
2番目のコードではスタックポインタを減算し、指された領域をコンストラクタやデストラクタの呼び出しで使用しているのでアクセス違反が発生したと考えられるでしょう。
cotora さんが書きました:また、今回のようにクラスメンバに大きなサイズの配列を用いたい場合はどのようにするのが良いのでしょうか。
インスタンスを動的確保するか、

コード:

class Cls{
private:
  char c[1000000][12];
public:
  Cls(){};
  ~Cls(){};
};

int main(){
  Cls *d = new Cls;
  delete d;
}
メンバを動的確保する(the rule of threeに従い、コピーコンストラクタと代入演算子も定義していることに注意)

コード:

#include <cstring>

class Cls{
private:
  char (*c)[12];
public:
  Cls(){
    c = new char[1000000][12];
  };
  Cls(const Cls& cls){
    c = new char[1000000][12];
    memcpy(c, cls.c, sizeof(char[1000000][12]));
  }
  Cls& operator=(const Cls& cls){
    memcpy(c, cls.c, sizeof(char[1000000][12]));
    return *this;
  }
  ~Cls(){
    delete[] c;
  };
};

int main(){
  Cls d;
}
といいでしょう。
また、配列を使用することにこだわらないのであれば、std::vectorに置き換えるという方法もいいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

cotora
記事: 4
登録日時: 8年前

Re: 大きなサイズの配列とEXC_BAD_ACCESSについて

#3

投稿記事 by cotora » 8年前

みけCATさん、

ありがとうございます。
とてもわかりやすくて、助かりました!
(確かにstatic char c[1000000][12]にするとEXC_BAD_ACCESSがでなくなりました。)
インスタンスの動的確保も、目から鱗でした。

閉鎖

“C言語何でも質問掲示板” へ戻る