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

void reprint(char *a[]) {
    if(*a) {
            printf("%d ",a);
            reprint(a+1);
            printf("%s ",*a);
    }
}

int main() {
    char *coll[] = {"C", "Objective", "like", "don't", "I", NULL};
    reprint(coll);
    printf("\n");
    return EXIT_SUCCESS;
}

より経験豊富な人が知っているように、これは配列を逆に印刷します。どうすればいいのかよくわかりません!

何をするのか理解するのに助けが必要reprint(char *a[])です。ポインタ演算はある程度理解できますがprintf、あちこちにsを挿入することで、関数が配列の最後まで増分し、次に最初に戻って、途中で印刷するだけであると判断しました。しかし、私はそれがどのようにこれを行うのか理解していません。実際のコードを見て理解できたのは、そう*aでない場合はNULL、次のインデックスで再印刷を再度呼び出すことだけです。

4

5 に答える 5

6

関数の出力を理解するための鍵は、再帰する前にポインターを出力し、再帰後に実際の文字列を出力することです。それはあなたに二度経験した印象を与えるものです。

これはばかげているように聞こえるかもしれませんが、プログラムの実行を手動で実行してください(またはデバッガーを使用してください)。それが再印刷関数に入ると、それはその後それ自体への呼び出しをヒットするprintf("%d ",a);ので、それは最初にNULLまでずっと「登る」でしょう。printf("%s ",*a);そうして初めて、シリーズに出会うでしょう。

このようにプログラムを変更すると、何が起こっているのかを理解するのに役立ちます。

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

static int callcounter = 1;

void reprint(char *a[]) {
    printf ("Entering reprint for the %d time\n",callcounter++);
    if(*a) {
            printf("%p ",a);
            reprint(a+1);
            printf("%s ",*a);
    }
    printf ("Exiting reprint\n");
}

int main() {
    char *coll[] = {"C", "Objective", "like", "don't", "I", NULL};
    reprint(coll);
    printf("\n");
    return EXIT_SUCCESS;
}
于 2012-06-10T10:05:13.853 に答える
3

関数を再帰的に呼び出すと、スタック上の関数の残りの部分へのポインターが残ります。Yoyは、再帰呼び出しが完了した後に実行することのリストとしてそれを考えるかもしれません。

最初、このスタックは空であると見なされる場合があります。一連の呼び出しの後、次のようなスタックが得られます。

    1st call: reprint ("C", "objective", "like", "don't", "I", NULL);
    2nd call: reprint ("objective", "like", "don't", "I", NULL);printf("C");
    3rd call: reprint ("like", "don't", "I", NULL);printf("objective");
                                                   printf("C"); 
    ...
    6th call: reprint(NULL); printf("I");printf("don't");printf("like");
                             printf("objective");printf("C");

これでスタックが巻き戻され、各文字列が正しい順序で出力されます。

于 2012-06-10T10:12:44.447 に答える
1

再帰はスタックを使用します。スタックは、LIFOの動作を備えたデータ構造です。reprint実際に単語を印刷する前に6回呼び出されます。

于 2012-06-10T10:08:04.967 に答える
1

このタイプの再帰を理解するための鍵は、スタックがどのように機能するかを知ることです。

まず、reprint()が(a + 1)で自分自身を呼び出すたびに、a+1がスタックにプッシュされます。これは、呼び出されたreprint()が引数としてchar**のコピーを取得することを意味します。呼び出し元の再版の「a」は変更されません。あなたが言ったように、これは渡されたchar **がNULL、つまり配列の最後の要素になるまで続きます。次に、テスト'if(* a)'がfalseになり、再印刷はそれ以上呼び出されなくなります。

この時点で、再印刷の5つの呼び出しはすべて、それが戻るのを「待機」しているため、再印刷の再帰呼び出しの後のprintfはまだ呼び出されていないことに注意してください。また、5つの呼び出しすべてに独自の「a」ポインタがあることに注意してください。これは不可欠です。

さて、最後の再印刷の呼び出しはもう再印刷を呼び出さないので、それはただ戻るでしょう。最後から2番目の再印刷(文字列「I」を指す「a」を持つもの)は、再印刷の呼び出し後の次のステートメント、つまり「I」のprintfに進むことができます。これが完了すると、この関数も戻ります。最後から3番目の再印刷で、「a」が文字列「do n't」を指しているものも続行でき、「don't」などを印刷します。

于 2012-06-10T10:25:31.437 に答える
0

* a!= 0(NULL)の場合、再印刷の呼び出しはa + 1で発行されます。これは、char配列の次の場所を意味し、最後の要素(NULL)が呼び出されるまで、関数はその戻りアドレスに戻ります。行:printf( "%s"、* a); このフレームでは、* aは「I」であり、関数は実行を終了し、同じ行の「戻りアドレス」に戻ります。このアクティブ化フレームでは、*aは「しない」というように戻ります。アドレスはprintf( "\ n");です。メインを実行して終了する行

于 2012-06-10T10:36:30.097 に答える