0

さて、これをまっすぐにしましょう。現在、私は Zed の Shaw Learn C と難しい方法で戦っていて、彼の演習 26 でかなり行き詰っています。この for ループがどのように機能し、それらが var arg システムにどのように関連しているかを誰かが説明してくれますか?

*File shell.c*

include "shell.h"
include "dbg.h"
include <stdarg.h>

int Shell_exec(Shell template, ...)
      {
    apr_pool_t *p = NULL;
    int rc = -1;
    apr_status_t rv = APR_SUCCESS;
    va_list argp;
    const char *key = NULL;
    const char *arg = NULL;
    int i = 0;

    rv = apr_pool_create(&p, NULL);
    check(rv == APR_SUCCESS, "Failed to create pool.");

    va_start(argp, template);

    for(key = va_arg(argp, const char *);
        key != NULL;
        key = va_arg(argp, const char *))
    {
        arg = va_arg(argp, const char *);

        for(i = 0; template.args[i] != NULL; i++) {
            if(strcmp(template.args[i], key) == 0) {
                template.args[i] = arg;
                break; // found it
            }
        }
    }

    rc = Shell_run(p, &template);
    apr_pool_destroy(p);
    va_end(argp);
    return rc;

error:
    if(p) {
        apr_pool_destroy(p);
    }
    return rc;
}

int Shell_run(apr_pool_t *p, Shell *cmd)
{
    apr_procattr_t *attr;
    apr_status_t rv;
    apr_proc_t newproc;

    rv = apr_procattr_create(&attr, p);
    check(rv == APR_SUCCESS, "Failed to create proc attr.");

    rv = apr_procattr_io_set(attr, APR_NO_PIPE, APR_NO_PIPE,
            APR_NO_PIPE);
    check(rv == APR_SUCCESS, "Failed to set IO of command.");

    rv = apr_procattr_dir_set(attr, cmd->dir);
    check(rv == APR_SUCCESS, "Failed to set root to %s", cmd->dir);

    rv = apr_procattr_cmdtype_set(attr, APR_PROGRAM_PATH);
    check(rv == APR_SUCCESS, "Failed to set cmd type.");

    rv = apr_proc_create(&newproc, cmd->exe, cmd->args, NULL, attr, p);
    check(rv == APR_SUCCESS, "Failed to run command.");

    rv = apr_proc_wait(&newproc, &cmd->exit_code, &cmd->exit_why, APR_WAIT);
    check(rv == APR_CHILD_DONE, "Failed to wait.");

    check(cmd->exit_code == 0, "%s exited badly.", cmd->exe);
    check(cmd->exit_why == APR_PROC_EXIT, "%s was killed or crashed", cmd->exe);

    return 0;

error:
    return -1;
}

Shell CLEANUP_SH = {
    .exe = "rm",
    .dir = "/tmp",
    .args = {"rm", "-rf", "/tmp/pkg-build", "/tmp/pkg-src.tar.gz",
        "/tmp/pkg-src.tar.bz2", "/tmp/DEPENDS", NULL}
};

Shell GIT_SH = {
    .dir = "/tmp",
    .exe = "git",
    .args = {"git", "clone", "URL", "pkg-build", NULL}
};

Shell TAR_SH = {
    .dir = "/tmp/pkg-build",
    .exe = "tar",
    .args = {"tar", "-xzf", "FILE", "--strip-components", "1", NULL}
};

Shell CURL_SH = {
    .dir = "/tmp",
    .exe = "curl",
    .args = {"curl", "-L", "-o", "TARGET", "URL", NULL}
};

Shell CONFIGURE_SH = {
    .exe = "./configure",
    .dir = "/tmp/pkg-build",
    .args = {"configure", "OPTS", NULL},
};

Shell MAKE_SH = {
    .exe = "make",
    .dir = "/tmp/pkg-build",
    .args = {"make", "OPTS", NULL}
};

Shell INSTALL_SH = {
    .exe = "sudo",
    .dir = "/tmp/pkg-build",
    .args = {"sudo", "make", "TARGET", NULL}
};
}
4

1 に答える 1

0

通常、Shell_exec を呼び出すときは、次のように呼び出します。

Shell_exec(foo, bar, baz);

その後、あなたは

va_start(argp, template);

これは、コンピューターに「わかりました。FOO (または関数内の「テンプレート」) の後から始めて、他のすべてを argp 変数に入れ、argp 変数を NULL で終了します」と伝えます。

したがって、argp 内では次のようになります。

argp == [&bar, &baz, NULL]

for ループに到達すると、va_arg が行うことは、argp から最初のエントリを削除してから、bar の場所に移動することです。ただし、実際には bar が何であるかを認識していないため、アドレスに含まれる変数の型を va_arg に伝える必要があるため、argp は現在

argp == [&baz, NULL]
key == bar

ただし、ループ自体に別の va_arg があることに気付きました! それは argp から別の変数を消費します!

argp == [NULL]
key == bar
arg == baz

ループの次の反復でループが終了します。

argp == []
key == NULL

for ループは Shell_exec から 2 つの引数を消費するため、Shell_exec を呼び出すときは常に奇数個の引数が必要であることに注意してください (1 つのテンプレート引数、その後は常に偶数)。

その背後にある実際のアイデアは、Shell_exec を呼び出すときに、次のように呼び出すことです。

Shell_exec(TAR_SH, "TARGET", "./install/file/location"); 

TAR_SH 構造を見て、デフォルトの引数「TARGET」があることに気付き、それを「./install/file/location」に置き換えます。置き換えたい引数の数だけ繰り返してから、結果の変更された TAR_SH を他の関数に送信します。

お役に立てれば。

于 2013-09-13T18:26:37.427 に答える