web-dev-qa-db-ja.com

main()の2番目の引数のchar * argv []とchar ** argvの違い

コード1

#include<stdio.h>

int main(int argc, char *argv[])
{
int j;
printf("%d", argv[1][0]);
return 0;
}

コード2

#include<stdio.h>

int main(int argc, char **argv)
{
int j;
printf("%d", argv[1][0]);
return 0;
}

CODE 1およびCODE 2は両方とも同じ出力を提供します。ただし、CODE 1とCODE 2のメイン関数のargument 2は異なります。ポインターの配列は、コンパイル時にデータセクションの上に作成されます。 argvはポインターの配列です。次に、メイン関数で引数を文字へのポインターへのポインターとして宣言する必要があります。つまり、** argv。コード1のように宣言するのはどうですか?

15
Jhansi Rani

Cの基本はchar** xおよびchar* x[]は、同じことを表す2つの方法です。両方とも、パラメーターがポインターの配列へのポインターを受け取ることを宣言します。いつでも書くことができることを思い出してください:

 char *parray[100];
 char **x;

 x = &parray[0];

そして、xを同じように使用します。

13
bmargulies

基本的に、char * argv []はcharポインターの配列を意味し、char ** argvはcharポインターへのポインターを意味します。

どの配列でも、配列の名前は配列の最初の要素へのポインターです。つまり、最初の要素のアドレスが含まれています。

したがって、以下のコードでは、char配列xで、xは文字である最初の要素「1」へのポインターです。したがって、それは文字へのポインタです。

また、配列arrでは、arrはポインターの最初の要素xであり、それ自体が文字へのポインターです。したがって、別のポインターへのポインターです。

したがって、xはchar *、arrはchar **です。

関数で何かを受け取る際の基本的なルールは、受け取るもののタイプを伝える必要があるということです。したがって、char **を受け取りたいと単純に言うか、char * arr []と言うこともできます。

最初のケースでは、複雑なことを考える必要はありません。単純に、char *の配列を受け取っていることを知っています。これを知らないの?それで、私たちはそれを受け取り、それを使用します。

2番目のケースでは、arrはchar **であると上で説明したように簡単です。これをタイプとして入れて安全に受け取ることができます。これでシステムは受け取ったもののタイプを認識し、配列注釈を使用するだけで次の要素にアクセスできます。配列の開始アドレスを受け取ったので、必ず次の要素に進むことができ、型がわかると、それに含まれるものとそれをさらに使用する方法がわかります。 charへのポインタが含まれていることがわかっているので、それらにも合法的にアクセスできます。

void func1(char* arr[])
{
    //function body
}
void func2(char** arr)
{
    //function body
}

int main()
{
    //x, y and z are pointer to char
    char x[3]={'1', '2', '3'};
    char y[3]={'4', '5', '6'};
    char z[3]={'7', '8', '9'};

    //arr is pointer to char pointer
    char* arr[3]={x, y, z};

    func1(arr);
    func2(arr);
}
12

それらはまったく同じです。 C11標準状態の§5.1.2.2.2:

プログラムの起動時に呼び出される関数の名前はmainです。実装は、この関数のプロトタイプを宣言しません。 intの戻り値の型で、パラメータなしで定義されます:

int main(void) { /* ... */ }

または2つのパラメーター(ここではargcおよびargvと呼びますが、宣言されている関数に対してローカルであるため、任意の名前を使用できます):

int main(int argc, char *argv[]) { /* ... */ }

または同等;10) または他の実装定義の方法で。

10)したがって、intintとして定義されたtypedef名に置き換えることができます。また、argvのタイプはchar ** argvとして記述できます。

明らかに、両方の宣言が同一であることを意図しています。その上、規則は§6.7.6.3/ 7で説明されています:

''type ''の配列としてのパラメーターの宣言は、 ''type ''への修飾ポインターに調整されます。型修飾子(ある場合)は、配列型の派生の[および]内で指定されます。 ...

6
user3920237

このような配列を宣言する

char array[]

constになります。つまり、次のコードを取得できません。

char array[] = "hello";
array = "hey";

2番目の文字列が小さく、このエラーが収まるはずです

エラー:配列型 'char [6]'は割り当てられません

あなたが持っている場合 **argv あなたは書ける

main(int argc, char **argv)
{
    char **other_array;
    /*
     * do stuff with other_array
     */
    argv = other_array;
}

あなたが持っている場合 *argv[]その後

main(int argc, char *argv[])
{
    char **other_array;
    /*
     * do stuff with other_array
     */
    argv = other_array;
}

あなたにこの警告を与えます

警告:「char **」から「const char **」に割り当てると、ネストされたポインター型の修飾子が破棄されます

したがって、技術的にはconstを記述したかのように小さな最適化です

2
jde-chil