1

問題: 2D配列にファイルの値を入力する関数をCで作成しようとしているとします。ファイルには、行(レコード)に配置された値が含まれ、各行にはいくつかのフィールドが含まれます。この関数は、2D配列へのポインターとファイルのアドレスを受け取り、配列に入力する必要があります。重要なことに、この関数は、レコードごとに存在するフィールドの数に関係なく機能する必要があります。たとえば、あるプログラムでは、関数を呼び出して、レコードごとに4つのフィールドがあるファイルから値を読み取ることができます。

int array_of_values[MAX_NUMBER_OF_RECORDS][4];
fill_in_array(array_of_values, "spacetime.csv");

別のプログラムでは、レコードごとに11個のフィールドがある場合に、値を入力することができます。

int array_of_values[MAX_NUMBER_OF_RECORDS][11];
fill_in_array(array_of_values, "M-theory.csv");

残念ながら、これを行おうとすると、Cが多次元配列を処理する方法に失敗します。多次元配列は、Cでは配列へのポインターの配列としてではなく、1つの長い1次元配列として実装されます。これは、関数が配列からデータを読み取るために、配列の幅を知る必要があることを意味します。

したがって、次の関数定義ではエラーが発生します。

void fill_in_array(int array_of_values[MAX_NUMBER_OF_RECORDS][], char *path)

[次の点で問題ないことに注意してください。

void fill_in_array(int array_of_values[][MAX_NUMBER_OF_RECORDS], char *path)

コンパイラは最初の次元のインデックスを知る必要はありませんが、これは許可されていないと仮定します(たとえば、関数がのような個々のレコードをいじくり回す必要がある場合array_of_values[1])。]

これが私のプログラムで到達したポイントです。自分自身を提示する2つの解決策があります:

  1. 関数を強制的に固定数のフィールドで動作させます。私は明らかにこれをしたくないのですが、たとえば、定数MAX_NUMBER_OF_FIELDSを宣言して未使用のフィールドを空のままにすることができます。
  2. fill_in_array関数が配列ではなくポインターを取り込んで、フィールドを含むIliffeベクトルを動的に割り当てるようにします。これは魅力的なアイデアです(レコード/フィールドの最大数を宣言する必要がなくなるためですが、フィールドの配列を解放する関数を作成する(そして使用することを忘れないでください!)必要があることも意味します。

もう1つのアイデアがあります。これは、関数の宣言を次のように変更することです。

void fill_in_array(int **array_of_values, int number_of_fields, char *path)

(ここでnumber_of_fieldsは、レコードごとのフィールド数を指しているため、と呼ぶ場合がありますfill_in_array(array_of_values, 4, "spacetime.csv");

array_of_valuesパラメータは明示的な配列ではなく、ポインタであることに注意してください。通常、2D配列を指すようにダブルポインタを割り当てた場合、結果は無意味になります。number_of_fields私の考えでは、関数が。のような式の処理方法を認識できるように、パラメーターを使用できる可能性がありますarray_of_values[i][j]

原則として、これはかなり簡単なはずです。実際、aが2D配列の場合、次のa[i][j]ように定義されます。

*(a + (i * n) + j)

ここnで、は配列の長さです。したがって、のすべての出現をで置き換え、すべての出現をで置き換えることarray_of_values[i][j]*(array_of_values + (i * number_of_fields) + j)できarray_of_values[i]ますarray_of_values + (i * number_of_fields)。ただし、このコードは非常に読みにくいでしょう。number_of_fieldsインデックス表記を使用して配列の要素にアクセスできるように、配列の幅が広いことをコンパイラに通知する方法はありますか?

4

4 に答える 4

3

いいえ、そのような方法はありません。

一般的な住所の計算が必要になったら、それを自分で実装する必要があります。

レコードごとのフィールド数を説明する明示的なパラメーターを追加するという解決策に到達したことをおめでとうございます。それは確かにそれが行われるべき方法です。

関数内でマクロを使用して、アドレス計算をより簡単に管理できるようにすることができます。

于 2013-03-25T15:39:04.963 に答える
1

いくつかの解決策があります。

構造体を使用します。

typedef struct {
  // whatever appears in a record
} record_t

void fill_in_array(record_t records[MAX_NUMBER_OF_RECORDS], const char* path);

これは、レコードのサイズがコンパイル時にわかっている場合にのみ意味があることに注意してください。この例では、そうではない場合があります。

ストライドを使用します。

void fill_in_array(int *array_of_values, int stride, const char *path)
{
  #define IDX(x, y) (x + (y * stride))

  // get the val at i,j
  int val = array_of_values[IDX(i,j)];

  #undef IDX
}

あなたはnumber_of_fieldsストライドであるあなたの関数でこのアプローチを提案しました、しかしストライドはあなたのコードを見ている他の開発者が認識する可能性が高い用語です。

無関係な点が1つあります。内容を変更しない場合は、変更するpath必要がありますconst:)

于 2013-03-25T15:43:10.237 に答える
0

あなたが探しているものはC++には存在しますが、Cには存在しないと私は信じています。C ++では、コンパイル時に既知のサイズの配列を操作するためのテンプレート関数を定義でき、残りはコンパイラーが処理します。Cには、2つのアプローチがあります。

  • サイズを明示的に定義するこれは、要素の数を指定する、
    のような関数の場合です。memcpy

    void process_array(int *data[], size_t max_x, size_t max_y)
    ....
    
  • 無効な数値を使用してサイズを定義する
    これは、strlenデータが特定の値で終了する関数の場合です('\0'ここ)
    。したがって、行列を使用して要素の数を可変にする関数が必要な場合は、でそれを示す方法を定義する必要があります。データ。

    #define ARRAY_TERM -1
    
    void process_array(int *data[])
    {
        size_t i, j;
        for (i = 0; data[i]; i++)
        {
            for (j = 0; data[i][j] != ARRAY_TERM; j++)
            {
               ...
            }
        }
    }
    ...
    

あなたがアイデアを持っていることを願っています。使い勝手が悪い。

別のアプローチがあります:あなた自身のタイプを定義してください。はい、多くの場合、実行可能なオプションです。

typedef struct array *array_t;
struct array
{
    size_t max_x, max_y;
    int *data;
};

それを操作するための関数の基本セット:

int array_init(array_t *a; size_t max_x, size_t max_y)
{
    array_t res;
    res = malloc(sizeof(*res));
    res->max_x = max_x;
    res->max_y = max_y;
    res->data = calloc(max_x * max_y, sizeof(int));
    *a = res;
    return 0;
}

void array_destroy(array_t *a)
{
    free((*a)->data);
    free(*a);
}

次に、操作用の追加機能を定義できます。

于 2013-03-25T16:06:34.223 に答える
0

C89(つまり、MSVCコンパイラ)に制限されていない限り、次のように多次元配列を渡すことができます。

#include <stdio.h>

void fill_in_array(size_t m, size_t n, int array_of_values[m][n])
{
  for (size_t i = 0; i < m; ++i) {
    for (size_t j = 0; j < n; ++j) {
      array_of_values[i][j] = ((i == j) ? 1 : 0);
    }
  }
}

void print_array(size_t m, size_t n, int array_of_values[m][n])
{
  for (size_t i = 0; i < m; ++i) {
    for (size_t j = 0; j < n; ++j) {
      printf(" %d", array_of_values[i][j]);
    }
    printf("\n");
  }
}

int main()
{
  {
    int array_of_values[2][4];
    fill_in_array(2, 4, array_of_values);
    print_array(2, 4, array_of_values);
  }
  {
    size_t h = 6, w = 5;
    int array_of_values[h][w];
    fill_in_array(h, w, array_of_values);
    print_array(h, w, array_of_values);
  }
}
于 2013-03-25T17:12:26.987 に答える