掲示板ランキング  靴下・ストッキング(かかとケア用靴下)


掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板1

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧

No.7030

動的に確保されたメモリへのアクセス
投稿者---蒼(2007/01/24 20:21:28)


こんばんは。

現在、行列演算をするプログラムを作っている所です。
動的に、二次元配列を確保するやり方はあっていると思うのですが、
動的に配列を確保する部分を、関数にしたので、
関数の外で、確保された領域へどうアクセスすればいいのかわかりません。
どなたか教えて頂けないでしょうか。

途中のプログラムは以下の通りです。

#include <stdio.h>
#include <stdlib.h>

double seisei1 (int m, int n);
double seisei2 (int m, int n);
void free1 (int m);
void free2 (int m);


int main(void)
{
    int m1, m2, n1, n2 = 0;
    
     printf("行列Aの行数を入力して下さい: \n");
     scanf("%d", &m1);
     printf("行列Aの列数を入力して下さい: \n");
     scanf("%d", &n1);
     printf("行列Bの行数を入力して下さい: \n");
     scanf("%d", &m2);
     printf("行列Bの列数を入力して下さい: \n");
     scanf("%d", &n2);

    
     seisei1(m1, n1);
      if (seisei1 == NULL) {
        printf("要素の確保に失敗しました");
        exit(EXIT_FAILURE);
     }
     seisei2(m2, n2);
      if (seisei2 == NULL) {
        printf("要素の確保に失敗しました");
        exit(EXIT_FAILURE);
     }
     
     free1(m1);
     free2(m2);
}

double seisei1 (int m, int n) {
    double **array1;    
    int i;
    
    array1 = (double **)malloc(sizeof(double) * m);
        if (array1 == NULL) {
            return NULL;
        }
    for(i = 0; i < m; i++) {
        array1[i] = (double *)malloc(sizeof(double) * n);
        if (array1[i] == NULL) {
            return NULL;
        }
    }
}

double seisei2 (int m, int n) {
    double **array2;    
    int i;
    
    array2 = (double **)malloc(sizeof(double) * m);
    if (array2 == NULL) {
        return NULL;
    }
    for(i = 0; i < m; i++) {
        array2[i] = (double *)malloc(sizeof(double) * n);
        if (array2[i] == NULL) {
            return NULL;
        }
    }
}

void free1 (int m) {
    int i;
   for(i = 0; i < m; i++) {
    free(array1[i]);
   }
   free(array1);
   
   exit(0);
}

void free2 (int m) {
    int i;
   for(i = 0; i < m; i++) {
    free(array2[i]);
   }
   free(array2);
   
   exit(0);
}



環境は、OSがウィンドウズXP,コンパイラがBorland C++ 5.5.1 for Win32です。

宜しくお願いします。






この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:動的に確保されたメモリへのアクセス 7032 akg 2007/01/24 21:07:20
<子記事> Re:動的に確保されたメモリへのアクセス 7034 かずま 2007/01/24 21:32:33
<子記事> Re:動的に確保されたメモリへのアクセス 7036 ルナレルナ 2007/01/25 11:35:48
<子記事> ありがとうございました 7041 2007/01/26 21:39:10


No.7032

Re:動的に確保されたメモリへのアクセス
投稿者---akg(2007/01/24 21:07:20)


> 動的に、二次元配列を確保するやり方はあっていると思うのですが、

本当ですか?
コンパイル時に、エラーや警告が出ますよ。


この投稿にコメントする

削除パスワード

No.7034

Re:動的に確保されたメモリへのアクセス
投稿者---かずま(2007/01/24 21:32:33)


#include <stdio.h>
#include <stdlib.h>

double **seisei(int m, int n);

int main(void)
{
    int m1, m2, n1, n2, i, j;  double **a, **b;

    printf("行列Aの行数を入力して下さい: \n");
    if (scanf("%d", &m1) != 1) exit(1);
    printf("行列Aの列数を入力して下さい: \n");
    if (scanf("%d", &n1) != 1) exit(1);
    printf("行列Bの行数を入力して下さい: \n");
    if (scanf("%d", &m2) != 1) exit(1);
    printf("行列Bの列数を入力して下さい: \n");
    if (scanf("%d", &n2) != 1) exit(1);

    a = seisei(m1, n1);
    b = seisei(m2, n2);
    if (!a || !b) printf("要素の確保に失敗しました"), exit(1);

    for (i = 0; i < m1; i++) {
        for (j = 0; j < n1; j++)
            printf(" %p", &a[i][j]);
        printf("\n");
    }
    puts("---");
    for (i = 0; i < m2; i++) {
        for (j = 0; j < n2; j++)
            printf(" %p", &b[i][j]);
        printf("\n");
    }

    free(a);
    free(b);
    return 0;
}

double **seisei(int m, int n)
{
    double *p;  int i;
    double **a = malloc(sizeof(double *) * m + sizeof(double) * m * n);
    if (!a) return NULL;
    for (p = (double *)(a + m), i = 0; i < m; i++, p += n) a[i] = p;
    return a;
}

double が 8の倍数のアドレスでないといけない処理系では修正が必要です。


この投稿にコメントする

削除パスワード

No.7038

Re:動的に確保されたメモリへのアクセス
投稿者---chu-(2007/01/25 12:20:00)


# 矩形配列とは以下のようなものを指すとします
#   a[0][0] a[0][1]
#   a[1][0] a[1][1]
#   a[2][0] a[2][1]
# そうでない配列
#   a[0][0] a[0][1]
#   a[1][0]
#   a[2][0] a[2][1] a[2][2]

# 1次元配列で疑似実装とは以下のようなものを指すとします
#   printf(" %p", &a1[i * n1 + j]);
# 2次元配列で実装
#   printf(" %p", &a2[i][j]);

2次元配列を確保しているのにmalloc()が1個所しかないのに興味を覚えてソースを読んでみました。
なるほど、後の領域拡張が必要無い+矩形配列であればこの方法はスマートに記述できていいですね。

図を書きながらソースを読んでいて、動的なN次元配列を実装するには
矩形配列であってもポインタ分の余分な領域が必要なことを改めて意識させられました。

そこでN次元矩形配列を1次元配列で疑似実装する方法を思い出しました。

それぞれどんなメリットデメリット、できることできないことがあり、どう使い分けるのがよいでしょうか。
自分では以下が思い浮かびました。

・2次元配列で実装するメリット
    ・構文糖を活用して読みやすく記述できる
        2次元配列で実装:     構文糖版:   printf(" %p", &a2[i][j]);
        2次元配列で実装:     非構文糖版: printf(" %p", (*(a2 + i) + j));
        1次元配列で疑似実装: 構文糖版:   printf(" %p", &a1[i * n1 + j]);
        1次元配列で疑似実装: 非構文糖版: printf(" %p", a1 + i * n1 + j);

・2次元配列で実装するデメリット
    ・ポインタ分の余分な領域が必要
    ・ポインタ分の余分な初期化が必要

上記を見る限り、動的N次元矩形配列は常に動的1次元配列で疑似実装すればよさそうに感じました。



この投稿にコメントする

削除パスワード

No.7039

Re:動的に確保されたメモリへのアクセス
投稿者---かずま(2007/01/26 09:54:12)


> 上記を見る限り、動的N次元矩形配列は常に動的1次元配列で疑似実装すればよさそうに感じました。

a[i][j] を a[i * n + j] と書かなければならなくて、掛け算が気になります。
C++ だと operator[] で見やすくできます。
#include <iostream>

template<typename T> class Mat {
    T *p;
    size_t n_;
public:
    Mat(size_t m, size_t n) : p(new T[n * m]), n_(n) { }
    ~Mat() { delete[] p; }
    T* operator[](size_t m) { return p + m * n_; }
};

int main()
{
    using std::cout;
    int m1 = 2, n1 = 3, m2 = 3, n2 = 4;
    Mat<double> a(m1, n1), b(m2, n2);
    for (int i = 0; i < m1; i++) {
        for (int j = 0; j < n1; j++)
            cout << ' ' << &a[i][j];
        cout << '\n';
    }
    cout << "---\n";
    for (int i = 0; i < m2; i++) {
        for (int j = 0; j < n2; j++)
            cout << ' ' << &b[i][j];
        cout << '\n';
    }
}



この投稿にコメントする

削除パスワード

No.7040

Re:動的に確保されたメモリへのアクセス
投稿者---chu-(2007/01/26 15:59:12)


返信ありがとうございます。

> a[i][j] を a[i * n + j] と書かなければならなくて、掛け算が気になります。

最初、速度的に気になるのかと誤読し、測定してしまいました。
その後、見た目的に気になるのだと認識しなおしましたが、せっかくなので貼り付けます。
1000x1000の配列の全要素へのデータ設定を1000回行っています。

> C++ だと operator[] で見やすくできます。

Cの範囲ではマクロを活用することになるのでしょうが、
データ構造を把握しづらくしてしまいそうなので私はそのままa[i*n+j]と書くかもしれません。

[test.c] 環境: Win2000, bcc32
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define SIZE 1000
#define N 1000

double *seisei1(int m, int n)
{
    double *a = malloc(sizeof(double) * m * n);
    if (!a) return NULL;
    return a;
}

double **seisei2(int m, int n)
{
    double *p;  int i;
    double **a = malloc(sizeof(double *) * m + sizeof(double) * m * n);
    if (!a) return NULL;
    for (p = (double *)(a + m), i = 0; i < m; i++, p += n) a[i] = p;
    return a;
}

int main(void)
{
    DWORD time;
    int x, y;
    int i;

    {
        double *a;
        a = seisei1(SIZE, SIZE);
        time = timeGetTime();
        for (i = 0; i < N; i++) {
            for (y = 0; y < SIZE; y++) {
                for (x = 0; x < SIZE; x++) {
                    a[y * SIZE + x] = i;
                }
            }
        }
        time = timeGetTime() - time;
        printf("time:%lu\n", time);
        free(a);
    }

    {
        double **a;
        a = seisei2(SIZE, SIZE);
        time = timeGetTime();
        for (i = 0; i < N; i++) {
            for (y = 0; y < SIZE; y++) {
                for (x = 0; x < SIZE; x++) {
                    a[y][x] = i;
                }
            }
        }
        time = timeGetTime() - time;
        printf("time:%lu\n", time);
        free(a);
    }

    return 0;
}

[出力]
time:34429
time:34420



この投稿にコメントする

削除パスワード

No.7036

Re:動的に確保されたメモリへのアクセス
投稿者---ルナレルナ(2007/01/25 11:35:48)
http://park6.wakwak.com/~nougaki/mini_program/


//
//	行列
//

#include <stdio.h>
#include <stdlib.h>

double **malloc_gyouretu(int m, int n);
void malloc_gyouretu2(double ***p_gyouretu, int m, int n);
void input_gyouretu(double **gyouretu, int m, int n);
void output_gyouretu(double **gyouretu, int m, int n);
void free_gyouretu(double **gyouretu, int m);

int main(void)
{
    double **gyouretu1, **gyouretu2;
    int m1, m2, n1, n2;
    
    printf("行列Aの行数を入力して下さい: \n");
    scanf("%d", &m1);
    printf("行列Aの列数を入力して下さい: \n");
    scanf("%d", &n1);
    printf("行列Bの行数を入力して下さい: \n");
    scanf("%d", &m2);
    printf("行列Bの列数を入力して下さい: \n");
    scanf("%d", &n2);
    
    gyouretu1 = malloc_gyouretu(m1, n1);
    gyouretu2 = malloc_gyouretu(m2, n2);
    
    //	malloc_gyouretu2(&gyouretu1, m2, n2);
    //	malloc_gyouretu2(&gyouretu2, m2, n2);
    
    input_gyouretu(gyouretu1, m1, n1);
    input_gyouretu(gyouretu2, m2, n2);
            
    output_gyouretu(gyouretu1, m1, n1);
    output_gyouretu(gyouretu2, m2, n2);
    
    free_gyouretu(gyouretu1, m1);
    free_gyouretu(gyouretu2, m2);
    
    return EXIT_SUCCESS;
}


double **malloc_gyouretu(int m, int n)
{
    double **gyouretu;
    int i;
    
    gyouretu = malloc(sizeof(double *) * m);
    if (gyouretu == NULL) {
        printf("要素の確保に失敗しました");
        exit(EXIT_FAILURE);
    }
    
    for (i = 0; i < m; i++) {
        gyouretu[i] = malloc(sizeof(double) * n);
        if (gyouretu[i] == NULL) {
            printf("要素の確保に失敗しました");
            exit(EXIT_FAILURE);
        }
    }
    
    return gyouretu;
}


void malloc_gyouretu2(double ***p_gyouretu, int m, int n)
{
    int i;
    
    *p_gyouretu = malloc(sizeof(double *) * m);
    if (*p_gyouretu == NULL) {
        printf("要素の確保に失敗しました");
        exit(EXIT_FAILURE);
    }
    
    for (i = 0; i < m; i++) {
        (*p_gyouretu)[i] = malloc(sizeof(double) * n);
        if ((*p_gyouretu)[i] == NULL) {
            printf("要素の確保に失敗しました");
            exit(EXIT_FAILURE);
        }
    }
}


void input_gyouretu(double **gyouretu, int m, int n)
{
    int i, j;
    
    for (i = 0; i < m; i++)
        for (j = 0; j < n; j++)
            gyouretu[i][j] = 0.0;	//	仮
            //	scanf("%lf", &gyouretu[i][j]);
}


void output_gyouretu(double **gyouretu, int m, int n)
{
    int i, j;
    
    for (i = 0; i < m; i++) {
        for (j = 0; j < n; j++)
            printf(" %f", gyouretu[i][j]);
        putchar('\n');
    }
    putchar('\n');
}


void free_gyouretu(double **gyouretu, int m)
{
    int i;
    
    for (i = 0; i < m; i++)
        free(gyouretu[i]);
        
    free(gyouretu);
}



この投稿にコメントする

削除パスワード

No.7037

Re:動的に確保されたメモリへのアクセス
投稿者---ルナレルナ(2007/01/25 11:59:33)
http://park6.wakwak.com/~nougaki/mini_program/


>    //	malloc_gyouretu2(&gyouretu1, m2, n2);

    //	malloc_gyouretu2(&gyouretu1, m1, n1);



この投稿にコメントする

削除パスワード

No.7041

ありがとうございました
投稿者---蒼(2007/01/26 21:39:10)


教えていただいたことを参考に
色々悪戦苦闘した結果、
きちんと動くようになりました。
皆さんお世話になりました。
有難うございました。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧





掲示板提供:(有)リアル・インテグリティ