我对这个 C 程序如何工作的理解是否正确?

Is my understanding of how this C program works correct?

在以下程序中:

#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
        char *delivery = "";
        int thick = 0;
        int count = 0;
        char ch;

        for (int i = 0; i < argc; i++) {
                fprintf(stdout, "Argv[%i] = %s\n", i, argv[i]); /* printing to understand (ptu) */
        }
        while ((ch = getopt(argc, argv, "d:t")) != -1)
                switch (ch) {
                        case 'd':
                                fprintf(stdout, "Optind in case 'd': %i\n", optind);
                                delivery = optarg;
                                break;
                        case 't':
                                fprintf(stdout, "Optind in case 't': %i\n", optind);
                                thick = 1;
                                break;
                        default:
                                fprintf(stderr, "Unknown option: '%s'\n", optarg);
                                return 1;
                }
        fprintf(stdout, "Argc: %i\n", argc); /* ptu */
        fprintf(stdout, "Argv: %p\n", argv); /* ptu */
        argc -= optind;
        argv += optind;
        fprintf(stdout, "Optind: %i. Argc after subtraction: %i, Argv after increment: %p\n", optind, argc, argv);
        if (thick)
                fprintf(stdout, "Thick crust!\n");
        if (delivery[0])
                fprintf(stdout, "To be delivered %s\n", delivery);
        fprintf(stdout, "Ingredients:\n");
        for (count = 0; count < argc; count++)
                fprintf(stdout, "%s\n", argv[count]);
        return 0;
}

当我 运行 使用下面显示的参数的上述程序时,我得到以下输出:

[u@h c]$ ./prog -t -d yesterday anchovies goatcheese pepperoni
Argv[0] = ./prog
Argv[1] = -t
Argv[2] = -d
Argv[3] = yesterday
Argv[4] = anchovies
Argv[5] = goatcheese
Argv[6] = pepperoni
Optind in case 't': 2
Optind in case 'd': 4
Argc: 7
Argv: 0x7ffebee8e498
Optind: 4. Argc after subtraction: 3, Argv index: 0x7ffebee8e4b8
Thick crust!
To be delivered yesterday
Ingredients:
anchovies
goatcheese
pepperoni

我想知道我对引擎盖下发生的事情的理解是否准确,特别是对于程序中的参数解析步骤。很抱歉没有分享一个更小的代表,但在这种情况下我可能不能。如果我能把这个展示给懂 C 的朋友,我就不会乱发 Whosebug。所以,请耐心等待。这里什么都没有:

  1. 定义 main 以接受命令行 (cl) 参数。这需要两个参数:

    • 整数 argc,它将包含 cl 参数的数量,包括程序名称,在本例中为 7
    • 字符串数组(即char指针数组),其中的每个元素将指向作为cl参数传入的每个字符串文字(存储在CONSTANT内存块中)的第一个元素的内存地址程序。
  2. for 循环(不言自明)

  3. 在 while 循环的每个 运行 上,getopt() 将解析 argv[] 数组并将 optstring "d:t" 中的下一个匹配字符分配给字符变量 ch,直到它 运行 没有选项(没有双关语意),那时它将 return -1 并且控件将退出 while 循环。

    • 在每次这样的传递中 optind(大概是因为 argv[0] 是程序名称而从 1 开始)将递增以包含要在 argv 中处理的下一个元素的索引...所以在 case 't', optind = <index of "-d" i.e. 2>case 'd', optind = <index of "anchovies" i.e. 4> 中(这是因为 getopt()optstring 中 'd' 之后的“:” 中意识到-d 将在命令行上跟随其 optarg,因此 optind 在此处递增为“4”而不是“3”)
    • 在处理 -t-d yesterday 之后 getopt()argv[] 中找不到与 optstring 中的元素匹配的任何其他内容;因此它 returns -1 并且我们跳出了 while 循环。 optind 仍然设置为 4,因为 getopt 在 optstring 的“-d”之后没有找到任何其他内容。
  4. 我们现在将 argcoptind 的值“4”递减,以确保我们跳过 option arguments(我们已经解析过)到剩下的三个 non-option 个参数。我们还递增 argv——它最初指向 argv[0] 的内存位置,即 "./prog"——递增 <optind * sizeof(char pointer on a 64-bit machine); i.e. 4 * 8>,这就是为什么 argv 现在指向内存中前面 32 个字节的原因:0x7ffebee8e4b8 - 0x7ffebee8e498 == 0x20.换句话说,argv[0] 指向“凤尾鱼”

  5. 然后我们根据 thickdelivery 的值打印内容,并循环遍历剩余的非选项参数以打印它们...

是的,你的理解是正确的。

我会提供两个注意事项:

  • “字符串文字”是指在您的程序中实际定义的字符串,作为 "" 定界符之间的字符。 “CONSTANT 块”不是标准概念,但我想你的意思是从二进制文件加载的 read-only 内存块,因为这是字符串文字通常所在的位置。 argv 指针指向的字符串不是这种类型;它们不可能,因为在创建二进制文件时它们是未知的。相反,它们位于某个未指定的内存区域,如果您愿意,您可以就地修改它们(尽管这可能会使您的代码混乱);例如argv[0][3] = 'x' 是合法的。 (C17标准5.1.2.2.1(2)).

  • 有些人可能同样会觉得在main中修改argcargv的值会造成混淆,并建议您将修改后的值赋值给一些其他变量代替:

int remaining_argc = argc - optind;
char **remaining_argv = argv + optind;