2

いつも私を困惑させることが 1 つありますva_end()。これは実際の関数ではなく、プリプロセッサ マクロであるとよく読みます。これは取るに足らない詳細のように聞こえるかもしれませんが、実際には、va_end()呼び出す必要がある場所に影響を与える可能性があります。

問題は単純です:複数のステートメントを持つ可変引数関数のva_end()各ステートメントの前に呼び出す必要がありますか?returnreturn

次の例の可変個引数関数には、その引数の最初の非 を返すという簡単なタスクがありNULLます。ご覧のとおりreturn、関数本体には 3 つのステートメントがあります。そのうちの 1 つva_start()が の前後に表示されva_end()ます。

このコードは正しいですか?

#include <stdio.h>
#include <stdarg.h>

static const void * const FIRST_NON_NULL_END = (void *) "";

void * first_non_null (
    const void * const ptr1,
    ...
) {

    if (ptr1) {

        return ptr1 == FIRST_NON_NULL_END ? NULL : (void *) ptr1;

    }

    void * retval;
    va_list args;
    va_start(args, ptr1);

    do {

        retval = va_arg(args, void *);

        if (retval == FIRST_NON_NULL_END) {

            /*  Is this correct? Here I do not invoke `va_end()`!  */
            return NULL;

        }

    } while (!retval);

    va_end(args);
    return retval;

}

int main () {

    const char * const my_string = first_non_null(
        NULL,
        NULL,
        "autumn",
        NULL,
        "rose",
        FIRST_NON_NULL_END
    );

    printf("The first non-null value is: \"%s\".\n", my_string);

    return 0;

}

編集:

明確にするために、上記の例は、教訓的な目的でのみその形式で書かれています。実際には、次のように、より適切で合成的な方法で書き直すことができます。

#include <stdio.h>
#include <stdarg.h>

static const void * const FIRST_NON_NULL_END = (void *) "";

void * first_non_null (
    const void * const ptr1,
    ...
) {

    const void * retval;
    va_list args;
    va_start(args, ptr1);

    for (retval = ptr1; !retval; retval = va_arg(args, const void *))
        ;;

    va_end(args);
    return retval == FIRST_NON_NULL_END ? NULL : (void *) retval;

}

int main () {

    const char * const my_string = first_non_null(
        NULL,
        NULL,
        "autumn",
        NULL,
        "rose",
        FIRST_NON_NULL_END
    );

    printf("The first non-null value is: \"%s\".\n", my_string);

    return 0;

}
4

1 に答える 1

4

C 標準から (7.16.1 可変引数リスト アクセス マクロ)

1 ... va_start マクロと va_copy マクロの各呼び出しは、同じ関数内の対応する va_end マクロの呼び出しと一致する必要があります。

したがって、va_startreturn ステートメントの前に使用されva_end、まだ呼び出されていない場合は、呼び出されます。

于 2021-09-03T20:05:22.217 に答える