共分散行列を2次元配列に格納しようとしているのですが、
その要素は、左下半分と右上半分が同じになるため
対角要素と、どちらか半分だけを格納してメモリを節約したいと考えています。
以下のように動的確保することでメモリは節約できているでしょうか?

if( (a = (double **)malloc(sizeof(double *) * (N) ))==NULL ){
fprintf(stderr, "error malloc for a\n");
exit(0);
}

for( i=0; i<NN; i++ ){
if( ( a[i] = (double *)malloc(sizeof(double) * (i+1) ))==NULL ){
fprintf(stderr, "error malloc for a\n");
exit(0);
}
}

*節約しない場合は、i+1 が N になります。

確保できているのなら、どのように参照すればいいのでしょうか?データの並び(?)は、a[0][0],a[1][0],a[1][1],a[2][0],a[2][1],a[2][2],,,というように並んでいるでしょうか?
例えば、a[0][1]を参照しようとすると、シグメンテーションフォルトなど起こりうるでしょうか。必要であれば、上プログラム内Nは、3000程度と考えてください。
そして、もし他にメモリを節約する上で良い方法があれば、ご教授していただけたらと思います。

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

このQ&Aに関連する最新のQ&A

A 回答 (6件)

> a[0][0]の次にa[1][0]が、a[1][1]の次にa[2][0]が、、、、a[i][i]の次に、a[i+1][0]が並んでいると考えてよろしいですか?


ということですが、
inthefloiさんの
> データの並びは、予想しているとおりに確保されています。
については、私は意見が異なります。ここら辺私が勉強したのはずい分と前の話ですから
ひょっとすると最近は違うのかもしれませんが、
私が見たmalloc()のコードでは、一回のアロケーション毎に管理データを作っていました。
ですから、このアルゴリズムでは、
 a[0] = malloc(...)
 a[1] = malloc(...)
 a[2] = malloc(...)
   :
という具合に呼んだら
 |a[0]管理データ|a[0][0]|a[1]管理データ|a[1][0]|a[1][1]|a[2]管理データ|a[2][0]|a[2][1]|a[2][2]|
とう具合に配置されると思います。
したがって、単純にデータのみが並んでいると考えることはできないということになります。
情報が古い分、「自信なし」です。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます!
上のinthefloiさんのご回答でも、そのようにおっしゃってますので、
自信持っていただいてよさそうです?

管理データの長さは、、と考えるよりも
素直に1次元配列に入れた方が良さそうですね。

勉強になりました。

お礼日時:2002/02/08 08:38

>データの並びについてですが、a[0][0]の次にa[1][0]が、a[1][1]の次にa[2][0]が、、、、a[i][i]の次に、a[i+1][0]が並んでいると考えてよろしいですか?



すいません。並びの部分をちゃんと読まずに、コードを見て、予想どおりと書いてしまいました。
malloc を繰り返した場合、それぞれが確保したメモリは連続しません。連続している事を前提とされるのでしたら、最初に ranx さんが書かれている通り、要素数 N*(N+1)/2 で malloc するしかありません。

その後、あえてテーブルを作るのであれば、参照するときのインデックス計算をあらかじめするだけです。

t = (double *)malloc(sizeof(double) * (N * (N + 1) / 2));
a = (double **)malloc(sizeof(double *) * N);
for (i = 0; i < N; i++) {
a[i] = &t[i * (i + 1) / 2];
}
    • good
    • 0
この回答へのお礼

三度の登場ありがとうございます!

一度にたくさんの質問を詰め込んでしまったので
そのような誤解をされるのも不思議ではありませんね。。
以後気を付けたいと思います。

後半部のコードは参考にさせていただきます。
また、質問出してたらよろしくお願いします!

お礼日時:2002/02/08 08:43

>#define COVXY(a,x,y)((x<=y?a[y][x]:a[x][y]))


>では、まずいでしょうか?

これは、私の趣味の問題かもしれません。
COVXYを左辺値としても有効にしたかったという事です。
右辺値としてだけ使うのならば、それでいいと思います。

>もし参照した場合はどうでしょうか。

OSやコンパイラによって結果は違うでしょうが、参照する場所が、OSから見て参照できない場所であれば、OSに怒れれるでしょう。ランタイムに悪影響があるか、何事もないか、別の場所が書き換えられなど、何が起こるかはわかりません。

>その他の疑問に関しては肯定していただけたと考えてよろしいですか?

データの並びは、予想しているとおりに確保されています。
メモリの節約という意味では微妙です。malloc の仕様や、OSの仕様によりますが、ほとんどの場合、節約はできているでしょう。
ranx さんが書かれているように、1度で確保すれば、確実に節約できます。
    • good
    • 0
この回答へのお礼

再度ご回答くださいましてありがとうございます!

確かに、おっしゃる定義の仕方の方が便利ですね。
勉強になりました。

データの並びについてですが、a[0][0]の次にa[1][0]が、a[1][1]の次にa[2][0]が、、、、a[i][i]の次に、a[i+1][0]が並んでいると考えてよろしいですか?
補足になるのですが、配列aを、並列プログラム内で、Bcast(全ジョブに転送)しようとしているのですが、その時に、先頭アドレスと、要素数を引数として与えなければなりません。この場合、データの並びが考えている通りだとすれば、要素数は、N*(N+1)/2で良いと思うのですが、いかがでしょうか。

もしよろしければ、もう少しお付き合いしていただけたら感激です。

お礼日時:2002/02/07 15:11

最後の部分のみ訂正。


a = (double*)malloc(sizeof(double)*n*(n+1)/2))
#define COVXY(a,x,y) ((x<=y)?a[(x)*((x)-1)/2+(y)]:a[(y)*((y)-1)/2+(x)])
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
お礼が遅くなり申し訳ありませんでした。

分かっていただけていたらいいのですが、
for文内のNN⇒Nに訂正です。

ここで、ranxさんがおっしゃるnはNと考えていいですね。
一次元配列でとる方が安全ですか。
プロフェッショナルな方は、
2次元配列はあまり用いないのでしょうか?

お礼日時:2002/02/07 14:59

とりあえず、Cとして(C++ではないものとして)お答えします。



> メモリは節約できているでしょうか?

一般的に言えば節約できていると推測されますけれど、アロケーションのアルゴリズムに依存するので、
確定的なことは言えないと思います。

> どのように参照すればいいのでしょうか?
inthefloiさんのやり方が合理的だと思います。

> データの並び(?)は、a[0][0],a[1][0],a[1][1],a[2][0],a[2][1],a[2][2],,,というように並んでいるでしょうか?
a[2][0],a[2][1],a[2][2]などはこの順に並んでいます。が、a[1][1]の次にa[2][0]が来るという保証はありません。

> a[0][1]を参照しようとすると、シグメンテーションフォルトなど起こりうるでしょうか
起こりえます。

> もし他にメモリを節約する上で良い方法があれば
a = (double**)malloc(sizeof(double)*n*(n+1)/2))
#define COVXY(a,x,y) a[(x)*((x)-1)/2+(y)]
なんてやり方も考えられますね。
    • good
    • 0

>a[0][1]を参照しようとすると



パフォーマンスの低下は仕方ないですが、アクセスの仕方を工夫してやればいいのじゃないかと思います。

#define COVXY(a, x, y) (*(x <= y ? &a[y][x] : &a[x][y]))

COVXY(a, 10, 32) = 1.0;
    • good
    • 0
この回答へのお礼

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

この場合、
#define COVXY(a,x,y)((x<=y?a[y][x]:a[x][y]))
では、まずいでしょうか?

おっしゃるように工夫をすればa[0][1]などを参照せずに済むとは思うのですが、
もし参照した場合はどうでしょうか。
その他の疑問に関しては肯定していただけたと考えてよろしいですか?

お礼日時:2002/02/06 06:39

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aと関連する良く見られている質問

Qdc.TextOut(0 ,0 , *str) ;について

環境 WIN98 VC++6.0 MFC にて

パターンBはOKですが、パターンAだと不正な処理で落ちてしまいます。

どうしてなのかお教えください。

void CFffView::OnPaint()
{
CPaintDC dc(this);
//パターンA
CString* str ;
str = (CString*)("999");
dc.TextOut(0 ,0 , *str) ;

//パターンB
CString aaa ;
aaa = (CString)("999");
dc.TextOut(0 ,0 , aaa) ;
}

Aベストアンサー

両方ダメ。
Aのパターンで動くのは、たまたま。

CString aaa ;
aaa = "999";
dc.TextOut(0 ,0 , aaa) ;

これで十分。

あえてキャストするんだったら、
CString aaa ;
aaa = (LPCSTR)"999";
dc.TextOut(0 ,0 , aaa) ;


aaa=のところでは、ただの代入が行われているわけではありません。
オーバーロードされたオペレータが呼ばれています。


>str = (CString*)m_array.GetAt(i) ;

これは、m_arrayの要素にCString*を入れていて、初めて成り立つ式です。
値をいれているところと、m_arrayの宣言を確認してください。

str = (CString*)("999");
も、
aaa = (CString)("999");
も、リテラル文字列をつっこもうとしています。
リテラル文字列とCStringはまったく別物です。

Qfp = fopen(argy[1], "r");の[1]の意味は

https://oshiete.goo.ne.jp/qa/8940272.html
 の11行目に
fp = fopen(argv[1], "r");の[1]の意味が分かりません。
試したいのですが、ソフトがうまく動きません
 よろしくお願いいたします。

Aベストアンサー

前の例題も読みました。

大分苦戦しているようですが、配列については理解が進みましたでしょうか?
お答えしますと、最初のパラメータ文字列が代入されています。

古いC言語の約束でして大変有名なものです。

コマンドラインコンソールから実行ファイル名を書いて、
パラメータをスペースで区切って指定したとします。

このパラメータ文字列が[1]以降に入ります。
例として、"test"と言う名前の実行ファイルがあったとします。

例)> test test1 test2 3 4

このようにコマンドラインから入力し実行すると、

argc = 5
argv[0] = "test"
argv[1] = "test1"
argv[2] = "test2"
argv[3] = "3"
argv[4] = "4"

と文字列が入ってきます。

以下はサンプル。

void main( int argc , char *argv[])
{
if( argc < 2 )
{
// パラメータ指定がなかった場合の表示
printf( "test に続けて4つまでパラメータを入力できます\n");
exit(0);
}
if( argc > 5 )
{
 // パラメータが5つ以上あった場合の表示
printf( "5つ以上のパラメータは受け付けません\n");
exit(-1);
}

// 正常ルート
printf( "パラメータの数は%d個ですね?ニヤッ\n", argc-1);
printf( "最初のパラメータは%sでしょ?\n", argv[1]);
printf( "残りはargv[2]以降ですが、面倒なので表示しません\n");
}

と言う具合に、引数を活用できます。argv[0]には、実行ファイル名が入ります。
argc は実行ファイルの名前も含めてカウントされます。

プログラムによっては必要な引数の数が変わりますし、
ユーザーのコマンドラインからの入力ミスなどでパラメータがなかったり、
必要なパラメータが不足していたりしますので、
argcとargvを使って最初にシンタックスエラーチェックをするのが常道です。

実行ファイル名の指定がなく、プログラムが実行されることはないと思われます
から、argcは1以上の値になります。

argc, argvの活用は、
実行コマンドを手打ちで打ち込むコンソール形式でよく用いられる基本的な
アプリケーションの開発手法です。

ご質問の箇所は、
第一パラメータにプログラム内で読み込むファイルのファイル名を指定してるの
でしょう。(よくあります)

しかし、Windowsなどのウィンドウを使用するアプリケーションは、
これとは違っておりますので注意が必要です。
(C言語とは別に、Windowsに特化した開発ノウハウの勉強が必要です)

Windows系で上記の様な基本的なプログラムを作成する場合は、
プロジェクトの作成時に(VisualStudioなどで)コンソールアプリケーション
を選んで作成します。実行時にコンソールが開きます。

Linuxの場合は、コンソールがデフォルトになっているでしょうから、
(特殊な設定がなければ)そのまま作成できます。

テキストエディタでソースを記述し、gcc などでコンパイルします。
実行形式ファイルが出来ていれば、想定どおりの動作をするでしょう。

ファイルの読み込みが出来るようになったら、
ファイルの内容を書き換えて保存したり、
ファイル名を変えたり、
ディレクトリ内のファイルを全て表示したり、
ファイル内に含まれる文字列を検索し、該当するファイルをリストしたりなど、

有用なサンプルプログラムを沢山作って練習します。

ファイルを読み込む先は、char型の配列でバイトサイズのメモリーとして確保
します。メモリーと変数の関係を充分に理解することをお勧めします。
殆どのプログラムは、このメモリーの確保やメモリーサイズの計算と格闘する
場合が多くなるからです。

バイナリー形式のファイル(すべてはバイナリー形式として良いのですが)に
ついて理解が深まった後は、
bmpの画像ファイル、wavなどの音声ファイルをあけて、
これの中身を書き換えて遊びます。

特にwavファイルは、音量の変更や周波数フィルタなども掛けれますので、
メモリ、配列、ファイルの関係を(焦らずに)ゆっくり理解するだけで、
今の知識レベルでも面白いことが沢山出来ます。

以上、ご参考に成れば。

前の例題も読みました。

大分苦戦しているようですが、配列については理解が進みましたでしょうか?
お答えしますと、最初のパラメータ文字列が代入されています。

古いC言語の約束でして大変有名なものです。

コマンドラインコンソールから実行ファイル名を書いて、
パラメータをスペースで区切って指定したとします。

このパラメータ文字列が[1]以降に入ります。
例として、"test"と言う名前の実行ファイルがあったとします。

例)> test test1 test2 3 4

このようにコマンドラインから...続きを読む

Qa[i]とa[i+1]を入れ替えるような関数を作成するC言語の問題

以下のように配列aとそのサイズsとインデックスiを引数
にとり、a[i]とa[i+1]を入れ替えるような関数を作成する問題。

但し、この関数内で配列の外側をアクセスしないようにエラー
チェックをすること。正常に処理が終了した場合は0を返し、
そうでない場合は-1を返すようにする。

main関数では、10個分の整数を入れる配列を宣言し、
データをユーザに入力させる。
次に、ユーザに1個整数を入力させ、0からその番号まで順に
iをずらしてswap_array関数を読んだ後、配列に入っているデータを
表示させる。

・配列にデータを入力でき、交換が正しくできている
・ユーザが配列の外側を指定した場合は、エラーである旨を
表示して終了する。

取りあえずこうゆうかんじになるのですが、//の前などをどうにすればいいか分かりません。分かる方は知恵を貸してください。

int swap_array(int a[], int s, int i)
{
//a[i]番目とa[i+1]番目を入れ替え処理をする

//正しく交換できた場合はreturn(0);
//エラーが発生した場合はreturn(-1);
}
int main()
{
//サイズ10の配列の宣言
int a[10];
//ユーザに10個のデータの入力をさせる
int i;
for(i=0;i<10;i++){
scanf("%d",&a[i]);
}
//0番目から何番目まで入れ替えるかユーザに聞く: (ユーザがnと入力)
int n;
......

//繰り返し構造を使って、0番目からn番目まで隣同士を交換
for(i=0;i<=n;i++){
int v;
// swap_arrayを繰り返し呼び出すが、毎回戻り値が0であることを
        //チェックしてエラーが発生していないことを確かめる
v=swap_array(a,10,i);

// エラーが発生していたら エラーが発生したことを表示して
//break;文で繰り返し構造から外にでる。
if(v==-1) break;
}

//エラーが発生しなかった場合に、入れ替えられた配列の内容を表示
if(v!=-1){
..... 配列の内容を表示
}

return(0);

以下のように配列aとそのサイズsとインデックスiを引数
にとり、a[i]とa[i+1]を入れ替えるような関数を作成する問題。

但し、この関数内で配列の外側をアクセスしないようにエラー
チェックをすること。正常に処理が終了した場合は0を返し、
そうでない場合は-1を返すようにする。

main関数では、10個分の整数を入れる配列を宣言し、
データをユーザに入力させる。
次に、ユーザに1個整数を入力させ、0からその番号まで順に
iをずらしてswap_array関数を読んだ後、配列に入っているデータを
...続きを読む

Aベストアンサー

こんな感じではだめでしょうか…。

#include <stdio.h>

unsigned int swap_array(int a[],unsigned int size,unsigned int s)
{
unsigned int temp,i=s;
if (size<s) return s;// 要素数を返す
while (i-1) temp=a[s-i+1],a[s-i+1]=a[s-i],a[s-i--]=temp;
return 0;
}
int main()
{
int a[10];
unsigned int n,i=0,size=sizeof(a)/sizeof(unsigned int);

do{
printf("%d個目のデータ?",i+1);
scanf("%d",&a[i]);
}while(++i<size);

//do{
printf("入れ替え対照、何番目のデータ(1~%d個)?",size);
scanf("%d",&n);
//}while(n>size || !n);
//この段階で範囲制限させる時は上記2行のコメントを解除

if (i=swap_array(a,size,n)) printf("エラー>要素数%dは範囲外です\r\n",i);
else for (i=0;i<size;i++) printf("%d個目:%d\r\n",i+1,a[i]);
return 0;
}

こんな感じではだめでしょうか…。

#include <stdio.h>

unsigned int swap_array(int a[],unsigned int size,unsigned int s)
{
unsigned int temp,i=s;
if (size<s) return s;// 要素数を返す
while (i-1) temp=a[s-i+1],a[s-i+1]=a[s-i],a[s-i--]=temp;
return 0;
}
int main()
{
int a[10];
unsigned int n,i=0,size=sizeof(a)/sizeof(unsigned int);

do{
printf("%d個目のデータ?",i+1);
scanf("%d",&a[i]);
}while(++i<size);

//do{
printf("入れ替え対照、何番目のデータ(1~%d個...続きを読む

Qfp = fopen(argv[1], "r");を”w" "a" "r+"・・・で試したらどうなる

http://oshiete.goo.ne.jp/qa/8897349.html
 以上のプログラムで
 以下をfp = fopen(argv[1], "r");の
 ”r"以外で以下を入れた場合の結果がどうなるかお聞きしたいです。
"r" 読み込みモード。ファイルが存在しているとする。
"w" 書き出しモード。すでにファイルがあれば内容を削除し、なければ新たに作成する。
"a" 追加モード。すでにファイルがあればその最後に追加し、なければ新たに作成する。
"r+" 更新モード。ファイルが存在しているとする。
"w+" 更新モード。すでにファイルがあれば内容を削除し、なければ新たに作成する。
"a+" 追加更新モード。すでにファイルがあればその最後に追加し、なければ新たに作成する。
 以上ですが、試す環境がございませんので、よろしくお願いいたします。

Aベストアンサー

お書きのとおりで合ってますよ。大丈夫です。

QSendMessage(hW,WM_CREATE,0,0);を

SendMessage(hW,WM_CREATE,0,0);
を実行するとシステムがWM_DOWNやWM_CHARを発行しなくなるみたいです
というのはそれ以降キー入力を無視するようになるのです
いったんアプリをアイコン化してウィンドウ化するとWM_DOWNやWM_CHARを発行するようになります
WM_CREATEを送ってもWM_DOWNやWM_CHARを発行しなくなるのを阻止するために何か方法はないでしょうか?

Aベストアンサー

>プログラムのイニシャライズのために送ったのですが送らないで住むプログラムに変更しました

普通はそんな方法はとりません。
システムが何をするか分からないからです。

自分でメッセージを定義して、初期化処理を行うようするためのメッセージを送るほうが無難です。
WM_CREATEと同じ処理を初期化処理として行わせたいのであれば、初期化処理を関数化して自分で定義したメッセージでも呼び出せばいいのですし。


>作ったプッシュボタンを押してシステムがWM_COMMANDを送ってきた後キー関係のメッセージを送ってくれなくなります

プッシュボタンがキーボードフォーカスを持ってのるでは?

ボタンがキーボードフォーカスを持っていてもキー関連のメッセージを親ウィンドウが受け取りたいのであれば、サブクラス化をするしかないでしょう。


人気Q&Aランキング

おすすめ情報