web-dev-qa-db-ja.com

二重ポインターvsポインターの配列(**配列vs *配列[])

私はそれらの違いが何であるかはっきりわかりません。私の教授は** arrayは* array []と同じであると書いており、私たちは彼が** arrayを使用する例を提示されました(したがって、クラスの後で* array [と交換した]そしてそれはうまくいきませんでした)、それらが実際に彼が書いたものと同じであるかどうか誰かに教えてもらえますか?とにかく、クラスは動的メモリ割り当てについてでした

@ダブルポインタを変更するとすぐに、この行はエラーをスローし始めました

    lines = malloc(sizeof(char*));

そして、メモリが再割り当てされている他のいくつかの

@ 2地獄そう、ここにコード全体があります

そして、以下のコメントについては、彼の発言が

    **array = *array[]

大きな更新

ご不便をおかけして申し訳ありません。この投稿を書いている時点で私はあまりにも疲れていました。ここに、編集なしのコード全体を示します

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

    char **lines;     // global text buffer, organized as an array of lines

    // --------------------------------------------------------------------------------
    // initialize global buffer
    void initialize()
    {
      lines = malloc(sizeof(char*));
      lines[0] = NULL;
    }

    // --------------------------------------------------------------------------------
    // return number of lines in buffer
    int countLines()
    {
      int count = 0;
      while(lines[count++]) ;
      return count-1;
    }

    // --------------------------------------------------------------------------------
    // print one line
    void printLine(int line)
    {
      printf("Line %d: %p %p %s\n",line, &lines[line], lines[line], lines[line]);
    }

    // --------------------------------------------------------------------------------
    // print all lines
    void printAll()
    {
      int num_lines = countLines();
      int line = 0;
      printf("----- %d line(s) ----\n",num_lines);
      while (line < num_lines)
        printLine(line++);
      printf("---------------------\n");
    }

    // --------------------------------------------------------------------------------
    // free whole buffer
    void freeAll()
    {
      int line = countLines();
      while (line >= 0)
        free(lines[line--]);
      free(lines);
    }

    // --------------------------------------------------------------------------------
    // insert a line before the line specified
    void insertLine(int line, char *str)
    {
      int num_lines = countLines();

      // increase lines size by one line pointer:
        lines = realloc(lines, (num_lines+2) * sizeof(char*));

      // move line pointers backwards:
      memmove(&lines[line+1], &lines[line], (num_lines-line+1)*sizeof(char*));

      // insert the new line:
      lines[line] = malloc(strlen(str)+1);
      strcpy(lines[line],str);
    }

    // --------------------------------------------------------------------------------
    // remove the specified line
    void removeLine(int line)
    {
      int num_lines = countLines();

      // free the memory used by this line:
      free(lines[line]);

      // move line pointers forward:
      memmove(&lines[line], &lines[line+1], (num_lines-line+1)*sizeof(char*));

      // decrease lines size by one line pointer:
        lines = realloc(lines, num_lines * sizeof(char*));
    }

    // --------------------------------------------------------------------------------
    // insert a string into specified line at specified column
    void insertString(int line, int col, char *str)
    {
      // make room for the new string:
      lines[line] = realloc(lines[line], strlen(lines[line])+strlen(str)+1);

      // move characters after col to the end:
      memmove(lines[line]+col+strlen(str), lines[line]+col, strlen(lines[line])-col);

      // insert string (without terminating 0-byte):
      memmove(lines[line]+col, str, strlen(str));
    }

    // --------------------------------------------------------------------------------
    // MAIN program
    int main()
    {
      initialize();

      printAll();
      insertLine(0,"Das ist");
      printAll();
      insertLine(1,"Text");
      printAll();
      insertLine(1,"ein");
      printAll();
      insertLine(2,"kurzer");
      printAll();
      printf("lines[2][4] = %c\n",lines[2][4]);
      insertString(2,0,"ziemlich ");
      printAll();
      removeLine(2);
      printAll();

      freeAll();
      return 0;
    }
12
Maxitj

質問で参照するコードが、ポインターへのポインターのポインター配列の使用例として教授から与えられた場合、そのクラスが実際にどれほど優れているかはわかりません。私はそれがデバッグ演習として提供されたか、それが解決策であなたの試みだったのではないかと思います。いずれにしても、単に警告を有効にしてコンパイルすると、コードのデバッグに進む前に注意が必要な多くの問題が見つかります。

参照するコードについては、グローバルテキストバッファを自由に使用できますが、グローバルバッファを使用せず、必要に応じてデータへのポインタを渡すことで、はるかに効果的になります。グローバルデータを必要とするいくつかのインスタンスやさまざまなコールバック関数などがありますが、経験則として、これらは例外であり、規則ではありません。

あなたの質問は基本的に「ポインタとダブルポインタ(ポインタからポインタへの型)変数の配列を適切に使用するにはどうすればよいでしょうか。1つの答えでトピックを完全にカバーする方法はありません。どちらか一方を使用できる(または使用する必要がある)状況とコンテキスト、およびその理由が多すぎますが、いくつかの例が基本的な違いを理解するのに役立ちます。

typeへのポインタの配列で始まります(例:_char *array[]_)。それは一般的にその形で関数の引数として見られます。変数として宣言すると、初期化が続きます。例えば。:

_char *array[] = { "The quick",
                  "brown fox",
                  "jumps over",
                  "the lazy dog." };
_

_char *array[];_の間に配列サイズがないため、変数宣言としての_[..]_自体は無効です。例のようにグローバルに使用すると、コンパイラは宣言を受け入れますが、宣言は警告1つの要素であると想定されます。

上記で宣言されたarrayの要素は、char型へのポインタです。具体的には、要素は宣言によって作成されたstring-literalsへのポインタです。各文字列には、array内の関連するポインタから_array[0], ... array[3]_としてアクセスできます。

タイプへのポインターへのポインター(ダブルポインター)は、その名前が示すとおりです。 ポインタを値として持つポインタです。基本的には、別のポインターを指すポインターです。次のようにarrayのアドレスを割り当てることにより、上記の配列のメンバーにアクセスするために使用できます。

_char **p = array;
_

ここで、_p[1]_または*(p + 1)は_"brown fox"_などを指します。

あるいは、タイプへのポインターへのポインターの数を動的に割り当てて、タイプへのポインターの配列を作成するために使用することができ、それを割り当てて再割り当てして、不明な数の要素のアクセスまたはストレージを処理することができます。たとえば、stdinから不明な数の行を読み取る簡単な例では、次のようになります。

_#define MAXL 128
#define MAXC 512
...
char **lines = NULL;
char buf[MAXC] = {0};
lines = malloc (MAXL * sizeof *lines);
size_t index = 0;
...
while (fgets (buf, MAXC, stdin)) {
    lines[index++] = strdup (buf);
    if (index == MAXL)
        /* reallocate lines */
}
_

上記のlinesは、char-to-pointer-to-char(最初はNULL)で、MAXL(128)のpointers-to-charを割り当てるために使用されます。次に、行がstdinからbufに読み取られ、読み取りが成功するたびに、bufの内容を保持するためにメモリが割り当てられ、メモリの各ブロックの結果の開始アドレスが割り当てられます各ポインタへの_line[index]_ここで、indexは_0-127_であり、indexから128に増加すると、indexが再割り当てされて追加のポインタと読み取りが提供されます続けます。

トピックが1つの回答で処理できるよりも大きくなるのは、ポインターの配列またはタイプへのポインターへのポインターが任意のtypeに対応できることです。 (intstruct、または異なるタイプの構造体のメンバーとして、またはfunctionなど...)これらを使用できますlinked-lists、ディレクトリ一覧の戻り(例:opendir)、またはその他の方法で。静的に初期化したり、動的に割り当てたり、関数のパラメーターとして渡したりすることができます...異なるコンテキストが多すぎてすべてをカバーできません。しかし、すべての場合において、それらはここと他の回答でここに見られる一般的なルールに従い、1,000のより多くの回答でここStackStackflowに従います。

最後に、配列とダブルポインターのさまざまな基本的な使用方法を確認するために使用できる短い例を示します。ソースにコメントを追加しました。これは、いくつかの異なる基本的な使用法、静的宣言と動的割り当てを提供するだけです。

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

int main (void) {

    /* array is a static array of 4 pointers to char, initialized to the 
       4 string-literals that a part of the declaration */
    char *array[] = { "The quick",
                    "brown fox",
                    "jumps over",
                    "the lazy dog." };
    /* p is a pointer-to-pointer-to-char assigned the address of array */
    char **p = array;
    /* lines is a pointer-to-pointer-to-char initialized to NULL, used
       below to allocate 8 pointers and storage to hold 2 copes of array */
    char **lines = NULL;
    size_t narray = sizeof array/sizeof *array;
    size_t i;

    printf ("\nprinting each string-literal at the address stored by\n"
            "each pointer in the array of ponters named 'array':\n\n");
    for (i = 0; i < narray; i++)
        printf (" %s\n", array[i]);

    printf ("\nprinting each string using a pointer to pointer to char 'p':\n\n");
    for (i = 0; i < narray; i++, p++)
        printf (" %s\n", *p);

    p = array;
    printf ("\nprinting each line using a pointer to pointer"
            " to char 'p' with array notation:\n\n");
    for (i = 0; i < narray; i++)
        printf (" %s\n", p[i]);

    /* allocate 8 pointers to char */
    lines = malloc (2 * narray * sizeof *lines);

    /* allocate memory and copy 1st 4-strings to lines (long way) */
    for (i = 0; i < narray; i++) {
        size_t len = strlen (array[i]);
        lines[i] = malloc (len * sizeof **lines + 1);
        strncpy (lines[i], array[i], len);
        lines[i][len] = 0;
    }

    /* allocate memory and copy 1st 4-strings to lines 
       (using strdup - short way) */
    // for (i = 0; i < narray; i++)
    //     lines[i] = strdup (array[i]);

    /* allocate memory and copy again as last 4-strings in lines */
    p = array;
    for (i = 0; i < narray; i++, p++)
        lines[i+4] = strdup (*p);

    p = lines; /* p now points to lines instead of array */
    printf ("\nprinting each allocated line in 'lines' using pointer 'p':\n\n");
    for (i = 0; i < 2 * narray; i++)
        printf (" %s\n", p[i]);

    /* free allocated memory */
    for (i = 0; i < 2 * narray; i++)
        free (lines[i]);
    free (lines);

    return 0;
}
_

ご不明な点がありましたらお知らせください。これは、非常に多くのさまざまな方法やさまざまなコンテキストで適用できる、比較的小さなルールセットを持つ大きなトピックです。

12
David C. Rankin

私の教授は**array*array[]と同じだと書いています

これは、一部のコンテキストでは当てはまり、他のコンテキストでは当てはまりません。

引数として関数で使用される場合、

void foo(int **array) {}

と同じです

void foo(int *array[]) {}

変数として宣言すると、

int **array;

と同じではありません

int *array[];

免責事項:これは、同じ場所と異なる場所の完全なリストではありません。

8
R Sahu