8

次の文章がそれに続くコードとどのように一致するかについて混乱しています。

argv は pointers の配列へのポインターであるため、配列にインデックスを付けるのではなく、ポインターを操作できます。この次のバリアントは、argc がカウントダウンされている間に、char へのポインターへのポインターである argv をインクリメントすることに基づいています。

#include <stdio.h>
/* echo command-line arguments; 2nd version */
main(int argc, char *argv[])
{
    while (--argc > 0)
        printf("%s%s", *++argv, (argc > 1) ? " " : "");
    printf("\n");
    return 0;
}

char *argv[]ポインターの配列だけではありませんか? ポインターの配列へのポインターは、char *(*argv[])または同様のものとして記述されませんか?

余談ですが、一般的に、配列とポインターが混在する宣言がかなり混乱するのは普通のことですか?

4

6 に答える 6

12

「配列へのポインター」または「配列を指す」などの用語は、C 用語ではかなり大まかに扱われることがよくあります。それらは、少なくとも 2 つの異なることを意味します。

この用語の最も厳密でペダンティックな意味では、「配列へのポインター」は「配列へのポインター」型で宣言する必要があります。

int a[10];
int (*p)[10] = &a;

上記の例pでは、 は 10 の配列へのポインターとして宣言されており、int実際にはそのような配列を指すように初期化されています。

ただし、この用語は、正式ではない意味でもよく使用されます。この例では

int a[10];
int *p = &a;

pへの単なるポインタとして宣言されていますint。array の最初の要素を指すように初期化されますa。この状況は以前の状況とは意味的に異なるにもかかわらず、pこの場合も s の「配列を指している」と人々が言うのをよく耳にしたり、見たりすることがあります。intこの場合の「配列へのポイント」は、p[5]またはのように、「ポインター演算を介して配列の要素へのアクセスを提供する」ことを意味します*(p + 3)

これはまさに、argvあなたが引用した「...はポインターの配列へのポインターです...」というフレーズの意味です。argvのパラメーター リスト内の の宣言mainは と同等です。char **argvつまり、argv実際にはポインターへのchar *ポインターです。しかし、char *(呼び出しコードによって保持される) ポインターの配列の最初の要素を物理的に指しているため、ポインターの配列を指しているというのはやや非公式に言うのが正しいargvです。

引用した文章の意味はまさにそれです。

于 2013-06-22T20:30:08.557 に答える
5

C 関数が配列を受け入れると主張する場合、厳密には代わりにポインターを受け入れます。言語は と を区別しませvoid fn(int *foo) {}void fn(int foo[])void fn(int foo[100])の配列を持っていて、それを渡すかどうかさえ気にしませんint [10]

int main(int argc, char *argv[])

と同じです

int main(int argc, char **argv)

したがって、ポインターargvの配列の最初の要素をchar指しますが、それ自体は配列型ではなく、(形式的に) 配列全体を指しているわけではありません。しかし、配列がそこにあることはわかっているので、その配列にインデックスを付けて他の要素を取得できます。

多次元配列を受け入れるなどのより複雑なケースでは[]、ポインターに戻るのは最初のものだけです (サイズを変更しないままにしておくことができます)。他のものは、指されている型の一部として残り、ポインター演算に影響を与えます。

于 2013-06-22T20:48:55.610 に答える
2

配列ポインターの等価性は、関数の引数に対してのみ真を保持するため、とは等価ですが、関数に渡したい変数に関しては真を保持しません。void fn(const char* argv[])void fn(const char** argv)

検討

void fn(const char** argv)
{
    ...
}

int main(int argc, const char* argv[])
{
    fn(argv); // acceptable.

    const char* meats[] = { "Chicken", "Cow", "Pizza" };

    // "meats" is an array of const char* pointers, just like argv, so
    fn(meats); // acceptable.

    const char** meatPtr = meats;
    fn(meatPtr); // because the previous call actually cast to this,.

    // an array of character arrays.
    const char vegetables[][10] = { "Avocado", "Pork", "Pepperoni" };
    fn(vegetables); // does not compile.

    return 0;
}

「vegetables」はポインターへのポインターではなく、3*10 の連続する文字シーケンスの最初の文字を直接指します。上記の fn(vegetables) を置き換えて取得します

int main(int argc, const char* argv[])
{
    // an array of character arrays.
    const char vegetables[][10] = { "Avocado", "Pork", "Pepperoni" };
    printf("*vegetables = %c\n", *(const char*)vegetables);

    return 0;
}

出力は「A」です。野菜自体は、間接的なポインターではなく、文字を直接指しています。

野菜の割り当ては、基本的にこれのショートカットです。

const char* __vegetablesPtr = "Avocado\0\0\0Pork\0\0\0\0\0\0Pepperoni\0";
vegetables = __vegetablesPtr;

const char* roni = vegetables[2];

に変換します

const char* roni  = (&vegetables[0]) + (sizeof(*vegetables[0]) * /*dimension=*/10 * /*index=*/2);
于 2013-06-22T22:03:55.370 に答える
0

「配列の最初の要素へのポインタ」は一般的な構造です。文字列を入力および出力する標準入出力関数を含め、すべての文字列関数がこれを使用します。main はそれを argv に使用します。

「配列へのポインター」はまれな構造です。C 標準ライブラリまたは POSIX でその使用法を見つけることができません。ローカルにインストールしたすべてのヘッダーを grep すると ( '([^)]*\*[^)]) *\['. (どちらも構造体のメンバーであり、関数のパラメーターではありませんが、それは重要ではありません。)

したがって、公用語に固執すると、短い名前の珍しいものと、長い名前の類似しているがはるかに一般的なものがあります. これは、人間の言語が自然に機能したい方法とは反対であるため、最も正式な状況を除くすべての状況で短縮名を「誤って」使用することで解決される緊張があります。

「ポインターからポインターへ」とだけ言わない理由は、関数パラメーターとしてのポインターの別の一般的な使用法があるためです。この場合、パラメーターは配列のメンバーではない単一のオブジェクトを指します。たとえば、

long strtol(const char *nptr, char **endptr, int base);

endptrargvはinとまったく同じ型でmain、どちらもポインターツーポインターですが、使用方法が異なります。s の配列のargv最初のものを指します。main 内では、、 などのインデックスで使用するか、 でインクリメントして配列をステップスルーすることが期待されます。char *char *argv[0]argv[optind]++argv

endptr単一の を指しchar *ます。内部では、インクリメントしたり、ゼロ以外の値を参照したりすることstrtolは役に立ちません。endptrendptr[n]n

その意味の違いは、「argv は配列へのポインターです」という非公式の用法によって表現されます。「配列へのポインター」が正式な言語で意味するものとの混同の可能性は無視されます。簡潔な言語を使用したいという自然な本能は、予約されているために最も明白な単純な句を使用しないように指示する正式な定義に固執したいという欲求よりも強いからです。ほとんど起こらない状況のために。

于 2013-06-22T21:04:43.720 に答える
0

Since argv is a pointer to an array of pointers.

これは間違っています。argvポインタの配列です。

于 2013-06-22T20:11:40.173 に答える
0

argv はポインターの配列へのポインターであるため、

いいえ、近くにもありません。

char *argv[]ポインターの配列だけではありませんか?

いいえ、それはポインターへのポインターです。

于 2013-06-22T20:12:16.397 に答える