学习 C 艰难的方式 ex 26 麻烦理解函数

  • 本文关键字:麻烦 函数 ex 方式 学习 c
  • 更新时间 :
  • 英文 :


好吧,让我们直截了当。目前,我正在与 Zed 的 Shaw 学习 C 进行艰难的战斗,我非常困在他的练习 26 中。问题是,我无法理解一个 var arg 函数中发生了什么 - Shell_exec。有人可以向我解释一下,这个 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}
};
}

通常,当你调用Shell_exec时,你会这样称呼它:

Shell_exec(foo, bar, baz);

然后你做

va_start(argp, template);

它告诉计算机"好的,从 FOO(或函数内的'模板')开始,将其他所有内容放在 argp 变量中,并以 NULL 结束 argp 变量"

所以,在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的两个参数,因此在调用 Shell_exec 时必须始终具有 ODD 参数数(一个模板参数,然后始终为偶数)。

它背后的实际想法是,当你调用Shell_exec时,你这样称呼它:

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

因此,您查看TAR_SH结构,请注意存在默认参数"TARGET",然后将其替换为"./install/file/location"。对要替换的参数重复此操作,然后将生成的修改TAR_SH发送到其他函数。

希望这有帮助。

最新更新