gpt4 book ai didi

c - 学习c的艰难之路前26个理解函数的麻烦

转载 作者:行者123 更新时间:2023-11-30 17:47:59 24 4
gpt4 key购买 nike

好吧,让我们开门见山吧。目前,我正在与 Zed 的 Shaw Learn 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 (或函数内的"template")开始,将其他所有内容放入 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 时必须始终使用奇数个参数(一个模板参数,然后始终为偶数)。

其背后的实际想法是,当您调用 Shell_exec 时,您可以这样调用它:

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

因此,您查看 TAR_SH 结构,注意到有默认参数“TARGET”,然后将其替换为“./install/file/location”。重复您想要替换的参数,然后将修改后的 TAR_SH 发送到其他函数。

希望这有帮助。

关于c - 学习c的艰难之路前26个理解函数的麻烦,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18746940/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com