こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

new と malloc によるメモリの動的確保について

初めて投稿させて頂きます。よろしくお願い致します。
現在、以下のような、2次元配列による動的なメモリの確保を意図して、コードをC/C++にて記述しています。
(コンパイラはMINGW32のg++ 3.4.5)

//mallocによるメモリ動的確保
data = (char **)malloc(num * sizeof(char*));
for(i=0; i<rowNum; i++){
data[i] = (char *)malloc(sizeof(char)*256);
}

//newによるメモリ動的確保
data = new char*[rowNum];
for(i=0; i<rowNum; i++){
data[i] = new char;
}

C++で書くのならば、
「mallocによるメモリ確保は辞め、newによるメモリ確保をしなさい」
という指摘が、書籍でもwebでもありましたので、
両方書き、両者を比べているのですが、理由がイマイチ分かりません。

10万行ほどのテキストデータで実験させてみたのですが、
mallocによる記述の方が、動作が数秒速いようなのです。

それで、new やmalloc で実際何をやっているのか、コードを見ようと思ったのですが、

newでは、

void* operator new(std::size_t) throw (std::bad_alloc);
void* operator new[](std::size_t) throw (std::bad_alloc);
void* operator new(std::size_t, const std::nothrow_t&) throw();
void* operator new[](std::size_t, const std::nothrow_t&) throw();
inline void* operator new(std::size_t, void* __p) throw() { return __p; }
inline void* operator new[](std::size_t, void* __p) throw() { return __p; }

というnewファイルの記述で行き詰まり、

malloc は malloc_allocator.hというファイルで行き詰りました。

以上を踏まえて、
1)そもそも、上記のメモリ動的確保記述はスマートな書き方なのか
2)実際に、newやmallocは、どういった手法でメモリ領域を確保しているのか

以上の2点について、ご教授下さい。よろしくお願い致します。

投稿日時 - 2009-10-29 23:38:08

QNo.5407432

困ってます

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

まあ, operator new[] は operator delete[] のための管理情報を追加しなきゃならないのでどうしても malloc より遅くなる (少なくとも「速くなることだけはない」) んですけどね....
どうしても速度が欲しいなら malloc, お手軽を求めるなら std::vector を使うのが普通かな. ああ, 今どき 3.4.5 もどうかと思うので 4.3.3 なり 4.4.2 にするってのも考えるべきかと.

投稿日時 - 2009-10-30 00:17:24

お礼

vectorで同様の処理を書いていたこともあったのですが、処理が重たくなり過ぎてしまったので、今はやっていません。

コード残しておけば良かったですが。。。どうやって書いてたかな。

MINGW32の標準は3.4.5で、4.x以降は開発版だとどこかで読んでいたので、3.4.5で開発していました。
4.xも検討してみます。

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

投稿日時 - 2009-10-30 01:57:15

ANo.1

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(9)

ANo.9

> ごめんなさい、理解できません。
> arrayの要素数は静的に決まるかと。

私自身、少し混乱があったようです。
何を考えていたかというと...

配列を動的に割り付けたい動機というのは、サイズを可変にしたいか、記憶域期間の問題をクリアしたいか、どのどちらかのケースが多いと思います。
そこで、後者の場合であれば、

array<array<char, 256>, 10>* data = new array<array<char, 256>, 10>;

で済むのではないかと考えていました。
ただ、最初の話の流れでは、

vector<vector<char> > data;

とすることを(私自身)想定していましたので、一貫性を失っていますね。
もちろん、vectorを使った場合でも、記憶域期間の問題をクリアするには、

vector<vector<char> >* data = new vector<vector<char> >;

とすることになるのでしょうが。

投稿日時 - 2009-10-30 23:03:40

お礼

実は記憶域期間の問題も抱えていて、別の形で質問させて頂こうと思っています。

STL等が十分に使える環境では、積極的にvectorを使ったほうが良さそうですね。

コメント頂いたキーワードから、知識の幅が広がりました。みなさん、ご回答ありがとうございました。

投稿日時 - 2009-11-01 01:36:18

ANo.8

> 単に動的に割り付けたいだけなら、十分かと思います。

ごめんなさい、理解できません。
arrayの要素数は静的に決まるかと。

投稿日時 - 2009-10-30 21:26:16

ANo.7

> arrayは固定長なのでmalloc/newの代わりにはなりません。

可変サイズにするならそうですね。
単に動的に割り付けたいだけなら、十分かと思います。

投稿日時 - 2009-10-30 19:51:30

ANo.6

> std::tr1::arrayでもよいと思います。

arrayは固定長なのでmalloc/newの代わりにはなりません。

投稿日時 - 2009-10-30 19:27:20

ANo.5

freeとdeleteを間違うようなプログラマは、(もっと紛らわしい)deleteとdelete[]を確実に使い分けられるとは思えません。
理想をいえば、そんなプログラマを使わないのが一番ですが、そうもいかないのであれば、配列形式のnewは全廃したほうがよいでしょう。
代わりはstd::vectorが最有力ですが、TR1が使える環境であれば、std::tr1::arrayでもよいと思います。std::vectorやstd::dequeを使う場合、push_backやinsert後に反復子が無効になるので、フールプルーフを考えると、std::tr1::arrayのほうがよいかもしれません。

投稿日時 - 2009-10-30 17:50:55

お礼

プログラムは専門ではないのですが、freeとdeleteはきちんと使い分けています。
配列のときは、delete [] x; として開放しています。

恥ずかしながら、TR1の存在は知りませんでした。
vectorやdequeについても、少し検索してみましたが、とてもデータを扱いやすそうですね。

勉強になります。ありがとうございます。

投稿日時 - 2009-10-30 22:19:24

ANo.4

> 「mallocによるメモリ確保は辞め、newによるメモリ確保をしなさい」
> という指摘が、書籍でもwebでもありましたので、
> 両方書き、両者を比べているのですが、理由がイマイチ分かりません。

newで確保したメモリはfree()で解放してはいけない
malloc()で確保したメモリはdeleteで解放してはいけない
という制約があります。
このため、1つのプロジェクトの中で、何の方針もなく
mallocによるメモリ確保とnewによるメモリ確保が入り乱れていると
後で解放する時にfreeすべきかdeleteすべきかを間違える可能性があり、
バグのもとになるとされています。

このことから、どちらかに統一すべきであり、
統一するなら、言語レベルでサポートされていて高機能である
newで統一すべきということが一般論として言われています。

(参考)
http://ja.wikipedia.org/wiki/Malloc#C.2B.2B.E3.81.A7.E3.81.AE.E5.88.A9.E7.94.A8

投稿日時 - 2009-10-30 09:34:47

お礼

wikipedia にmallocに関する記事があるとは、盲点でした。
勉強になりました。

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

投稿日時 - 2009-10-30 13:02:42

ANo.3

newはmallocには無い機能があります。
例えばインスタンスをnewで生成する場合には、
引数付のコンストラクタを呼び出せます。
Hoge *h = new Hoge(1);
他にはnewは失敗した場合、例外を投げることができます。
new[]は他の回答者さんの仰る通りvectorを使う方がお手軽ですが、
STLを使いたくない等の理由がある場合は、new[]を普通に使います。

投稿日時 - 2009-10-30 00:46:39

お礼

newにはそんな使い方もできたのですね。
これは知りませんでした。
malloc単体では例外を投げることが出来ないのも納得です。
STLは出来れば避けたいので、newでやってみようと思います。

ご回答、ありがとうございました!

投稿日時 - 2009-10-30 02:17:46

ANo.2

GCCの場合、operator newはmallocを用いて実装されています。mallocに失敗した場合にstd::set_new_handlerで登録した関数を呼び出し、(通常)例外を送出する処理を行うことになるため、どうしてもmallocより遅くなります。

C++ではmallocを使ってはいけないのではなく、非C互換型に対してmallocを使ってはならないと考えたほうがよいでしょう。ただし、正しく使い分けられないのであれば、newに統一したほうが無難なことは確かです。

あと、配列形式のnewも、どうしてもそれを使わなければならない理由がない限りは避けたほうがよいでしょう。普通はstd::vectorを使います。bool型のようにstd::vectorが適切でない場合には、std::dequeを使うとよいでしょう。

MinGWのバージョンに関しては、現状の最新版は4.4.0ですが、-finput-charsetだったか-fexec-charsetだったかが正しく動作しなかったように記憶しています。これでは日本語がまともに使えないので、古いバージョンを使ったほうがましでしょう。
自分でコンパイルし直す手間を惜しまないなら、それもよいとは思いますが...

投稿日時 - 2009-10-30 00:28:24

お礼

gccでは、newの中でもmallocを用いているんですね。
例外処理などで、安全性を高めるためにnewが実装されていると。
なるほど。

std::vectorだと、扱うデータが大きく処理速度が落ち過ぎるようなので、敬遠しています。std::dequeについては知りませんでしたので、勉強させて頂きます。

3.4.5でも、-finput-charsetや-fexec-charsetはきちんと動作してくれませんでした。今はその都度、エスケープしています。

逆に言えば、4.4.0にアップグレードしても、不安要素はそのくらいということですか・・・なら、アップグレードしてしまったほうがいいですね^^;

ご回答ありがとうございました!

投稿日時 - 2009-10-30 02:12:11

あなたにオススメの質問