【解決方法】(C言語)ポインタを使った多次元配列がわかりません


わかりません C言語について質問できれば、できなかったらごめんなさい。

これら 2 つのコードを理解しようとしました。

C
int *pv = (int*) malloc(5 * sizeof(int));
    for(int i=0; i<5; i++) {
        pv[i] = i+1;
    }

これは 1 次元配列です。
そして、私はその方法について混乱しています malloc 機能しますが、なぜポインタでもあるのでしょうか?? なぜなら、次のようなより単純な例があったからです。

C
#include 
int main() {

  int x[5] = {1, 2, 3, 4, 5};
  int* ptr;

  // ptr is assigned the address of the third element
  ptr = &x[2]; 

  printf("*ptr = %d \n", *ptr);   // 3
  printf("*(ptr+1) = %d \n", *(ptr+1)); // 4
  printf("*(ptr-1) = %d", *(ptr-1));  // 2

  return 0;
}

ここのポインタ ptr のアドレスを指しています x[2] これは &x と書かれます[2]。 では、なぜ今ポインターが指すのか malloc これもポインタですか?

二次元配列では、次の例を参照しました。

C
int rows = 2;
    int columns = 5;
    int **matrix = (int **) malloc(rows * sizeof(int *));
    matrix[0] = (int *) malloc(rows * columns * sizeof(int));
    for (int i = 1; i < rows; i++)
        matrix[i] = matrix[0] + i * columns;

ここに何が書いてあるかわかりません。 マトリックスが 2 回使用されるのはなぜですか? 一度として **matrix そして1つは matrix[0]? なぜ **matrix に変わった matrix[0]?

そうじゃない **matrix と同じ matrix[][] ??

この例のように:

C
#include 
int main() {

  int i, x[6], sum = 0;

  printf("Enter 6 numbers: ");

  for(i = 0; i < 6; ++i) {
  // Equivalent to scanf("%d", &x[i]);
      scanf("%d", x+i);

  // Equivalent to sum += x[i]
      sum += *(x+i);
  }

  printf("Sum = %d", sum);

  return 0;
}

ここ、 x[0] と言っているのと同じです *x。 それで matrix[][] と同じです **matrix
では、なぜ 2 つの違いがあるのでしょうか mallroc で使われる matrix[0] それは奇妙です **mallroc ??

最後の質問:

また、次のようなポインターの配列も理解できません。

C
#include 
 
int main()
{
 
    char* arr[3] = { "geek", "Geeks", "Geeksfor" };
 
    for (int i = 0; i < 3; i++) {
        printf("%s\n", arr[i]);
    }
 
    return 0;
}

配列は次のとおりです char そして char 単一文字しか保存できないのですが、どうすればよいですか? arr[0] ショー geek 単なる手紙ではありません」g「?そして、どうやって *arr 「の住所は何ですか?」geek「?

この例は次のことを示しています。

C
#include 
 
int main()
{
    // declaring some temp variables
    int var1 = 10;
    int var2 = 20;
    int var3 = 30;
 
    // array of pointers to integers
    int* ptr_arr[3] = { &var1, &var2, &var3 };
 
    // traversing using loop
    for (int i = 0; i < 3; i++) {
        printf("Value of var%d: %d\tAddress: %p\n", i + 1, *ptr_arr[i], ptr_arr[i]);
    }
 
    return 0;
}

ポインタの配列には変数のアドレスを入れる必要がありますが、 char *arrを入れただけです。 string 上記のようなアドレスではありません( &var 代わりに char arr がある “geek「これのアドレスではありません」 string)。

私が試したこと:

私は次のような多くのフォーラムを読んでみました。

しかし、それはとても混乱します。

解決策 1

もちろん、C について質問することはできます。それが 50 年前の言語であり、かなり古い言語であるからといって、C について質問できないというわけではありません。
ただし、これを第一言語として学習している場合は、就職にはほとんど役に立ちません。C# のようなより現代的な言語を選択するほうがよいでしょう。

まず知っておくべきことは、配列の名前は配列内の最初の要素へのポインターであるため、ポインター構文と配列インデックス構文を同じ意味で使用できることです。

C
#include <stdio.h>

int main()
    {
    printf("Hello World\n");
    int myArray[] = {1, 2, 3, 4, 5};
    int first = *myArray;
    int second = myArray[1];
    if (first == 1 && second == 2)
       {
       printf("Yes!\n");
       }
    else
       {
       printf("No!\n");
       }
    return 0;
    }

「Yes!」と出力されます。 毎回。

なぜですか malloc ポインタ? そうですね、そうではありません。これはポインタを返す関数ですが、それは違います。
malloc これは、必要なバイト数をパラメータとして受け取り、新しく割り当てられたメモリの最初のバイトへのポインタを返します。メモリをどうするかは知りませんし、気にせず、メモリを割り当てるだけです。プログラムに割り当てられたメモリ領域。 heap。 そして、それはと呼ばれるコンパニオン機能を持っています free 使い終わったらその記憶を解放します。 (電話する必要があります free または、アプリが「メモリ リーク」を開始します。ヒープは、割り当てられたがそれ以上は必要のない「メモリの塊」でいっぱいになります。)

また、メモリ ブロックは、文字列、整数、配列など、好きなように使用できます (必要に応じて、アプリのさまざまな部分で 3 つすべてとして使用できます)。

Malloc は、と呼ばれるものを返します。 void pointer – これは特別な型であり、直接使用することはできませんが、任意の型のポインターにキャストできます。 intint*、 あるいは char******* 本当に必要なら!

ポインタを取得したら、それを使用したり (逆参照と呼ばれます)、コピーしたり、キャストしたり、必要なことを何でも行うことができます。

2D 配列の例では、 matrix として宣言されます int** – 整数へのポインタへのポインタ – そして malloc に十分なスペースを割り当てるように求められます rows ポインタ:

C
int **matrix = (int **) malloc(rows * sizeof(int *));

さて、C は非常に古い言語なので、おそらく 32 ビット コードを生成します。つまり、ポインタも 32 ビット幅、つまり 4 バイトになります。 したがって、malloc は、次の内容を保持するメモリ ブロックを要求されます。 rows * 4 バイト。 次の行はおそらく少し混乱します。整数のすべての行と列を保持するのに十分な大きさのメモリ ブロックを割り当て、それを整数へのポインタの配列の最初の要素に割り当てます。
次に、ループは配列内の各行にポインターを追加します。
より良い書き方は次のようになります。

C
int **matrix = (int **) malloc(rows * sizeof(int *));
int *arrayData = (int *) malloc(rows * columns * sizeof(int));
for (int i = 1; i < rows; i++)
   {
   matrix[i] = arrayData + i * columns;
   }

なぜなら arrayData は整数へのポインタです。数値を追加すると、自動的に整数のサイズが計算に含まれるため、それについて心配する必要はありません。

matrix[0] 配列の最初の要素を返します。 matrix が整数へのポインタへのポインタである場合、整数へのポインタ (最初の行の最初の整数) を返します。

冒頭で「配列の名前は配列の最初の要素へのポインタである」と述べたのはそのためです。その結果、 *matrix そして matrix[0] は同じ意味で使用できます (ただし、通常は、これらを混同するよりも 1 つの構文に固執する方が適切です)。 同様に、 *(matrix + 1) そして matrix[1] は等価であり、配列の 2 番目の行へのポインタを返します。

必要な 2 つのメモリ ブロックを割り当てるには 2 つの malloc が必要です。1 つは「行の先頭」の配列に格納されます。 matrix もう 1 つは行と列のデータ自体を含むメモリ ブロック用です。

最後の質問では、 arr の配列ではありません chars – へのポインタの配列です。 char価値観。 arr[0] 「geek」の最初の文字へのポインタを返します、arr[1] 「Geeks」の最初の文字へのポインタを返します。以下同様です。
それで、あなたが電話するとき、 printf そしてそれを渡します arr[i] あなたは渡していません char しかし、へのポインタ char – したがって、単一の文字ではなく、文字列全体が出力されます。

どうやってそれがわかるのでしょうか? システムは 3 つの文字列にメモリを割り当て、コードの実行時に文字列リテラルをそれらの文字列にコピーします。 次に、3 つのポインターに十分なスペースを割り当て、割り当てたばかりの文字列を指すように設定し、最後に arr これら 3 つのポインタのうちの最初のポインタを指します。

混乱するのはわかっていますが、紙と鉛筆を用意する前に、少し考えてください。次に、しばらくシステムのふりをします。メモリのブロックを描画し、その「ビットを割り当てる」と、矢印を描くことができます。それを指すように変数を設定するとき。 アイデアはわかると思いますが、しばらく時間がかかり、かなりの量の「こすり出し」が必要になる場合があります。

これが、私が C が入門言語としては不十分であると述べた理由です。より現代的な言語 (たとえば C# など、たくさんありますが) では、ポインタについて心配する必要はありません。ポインタはすべて舞台裏で処理され、必要な処理を行うだけです。変数とその内容。

解決策 2

malloc (たとえば、次を参照してください: malloc – cppreference.com[^]) はヒープに割り当てますが、「より単純な例」では、配列はスタックに割り当てられます。
動的に割り当てられた 2 次元配列 (特に、 ギザギザの配列[^])その後、使用する必要があります malloc 2回。

良い C 本はそのギャップを埋めるのに役立つかもしれません。

解決策 3

これはポインタ自体の一般的な理解です。
ポインタはメモリのアドレスを含むことを意味します。
あなたの例には配列があります:
int x[5] = {1, 2, 3, 4, 5};

したがって、x は 5 つの整数要素を含むメモリ内の配列へのポインタです。
配列要素の値にアクセスするには、次のように括弧を使用します。[0]、 バツ[1] ただし、この要素のアドレスを取得するには、アンパサンドを使用する必要があります – 値への参照を取得します。これは例で行われます。 ptr = &x[2];

したがって、x は最初の要素へのポインタであり、 ptr は 3 番目の要素へのポインタです。
配列型の場合、ポインタをインクリメントすると次の要素に移動し、デクリメントすると前の要素に移動します。 したがって、あなたの例では次のようになります。 ptr + 1 – 次の要素と等しい &pts[1] そして同時にそれは &x[3]。 そしてその ptr - 1 前の要素です &pts[-1] または &x[1]
型付きポインターの値を取得するには、「*」文字を使用します。 *ptr が指すメモリから int 型のデータの値を取得します。 ptr これは次と等しい ptr[0]
同じメモリへのポインタが 2 つある場合は、ということを理解する必要があります。 したがって、あるポインタに値を設定すると、それらは同じアドレスを指すため、別のポインタにも同じ値が設定されます。 あなたの例に戻ります: *ptr = 10; printf("%d",x[2]); ptr は x と同じアドレスを持つため、出力に「10」を書き込みます。[2]、そして同時に x[2] = 10; printf("%d",*p) 同じ結果が得られます。
多次元配列に関して覚えておくべきことは、配列にはポインターが含まれているということだけです。 そして、あなたも同じように彼らと協力すべきです。

int matrix[3][3]; // array
int** pp = (int**)matrix; // pp point to the same array

例に戻ると、最初の malloc にポインタの配列へのポインタがあります。matrix は、動的に割り当てられるように、この配列にメモリを割り当てます。 そして 2 番目の malloc は実際のデータにメモリを割り当てます。 データのメモリを含む配列全体にデータを 1 回割り当てることができますが、その後はディメンションに基づいて適切に設定するだけで済みますが、型のキャストが必要になるため、これはより複雑になります。

int ** matrix = (int**)malloc(rows * sizeof(int*)+ rows * columns * sizeof(int));
matrix[0] = ((int*)matrix) + rows * sizeof(int*);

文字の配列は、文字へのポインタの配列と同じです。 char * arr[3]; したがって、単一の文字ではなく、ゼロで終わる文字列が存在します。 そして、コード内の printf 関数で “%s” を使用すると、そのような文字列が表示されます。
次の例では、ポインターの配列を埋めるための変数への参照演算子があります。
例から簡単に文字の配列を作成できます。

char * var1 = "geek";
char var2[] = "Geeks";
char var3[9] = "Geeksfor";
char* ptr_arr[3] = { &var1, &var2, &var3 };

あなたの質問はこれですべてのようです。
よろしく、
マキシム。

解決策 4

malloc は一部のメモリ ブロックを割り当てただけであり、 あなたの タスクはそのメモリを処理し、型付きポインタを使用してそれを操作します。

複雑な解決策の場合は、メモリの処理方法をユーザー (およびコンパイラー) に提供する構造体の使用をお勧めします。

常にポインタはあるメモリへのポインタにすぎません。 ない それが指し示す記憶。

新しい機能のサポートを向上させるために C++ を使用して、チュートリアルとその例に時間を費やしてみてください。 それは多くの頭痛を避けるでしょう。

コメント

タイトルとURLをコピーしました