我对这个 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。所以,请耐心等待。这里什么都没有:
定义 main 以接受命令行 (cl) 参数。这需要两个参数:
- 整数 argc,它将包含 cl 参数的数量,包括程序名称,在本例中为 7
- 字符串数组(即char指针数组),其中的每个元素将指向作为cl参数传入的每个字符串文字(存储在CONSTANT内存块中)的第一个元素的内存地址程序。
for 循环(不言自明)
在 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”之后没有找到任何其他内容。
我们现在将 argc
的 optind
的值“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] 指向“凤尾鱼”
然后我们根据 thick
、delivery
的值打印内容,并循环遍历剩余的非选项参数以打印它们...
是的,你的理解是正确的。
我会提供两个注意事项:
“字符串文字”是指在您的程序中实际定义的字符串,作为 ""
定界符之间的字符。 “CONSTANT 块”不是标准概念,但我想你的意思是从二进制文件加载的 read-only 内存块,因为这是字符串文字通常所在的位置。 argv
指针指向的字符串不是这种类型;它们不可能,因为在创建二进制文件时它们是未知的。相反,它们位于某个未指定的内存区域,如果您愿意,您可以就地修改它们(尽管这可能会使您的代码混乱);例如argv[0][3] = 'x'
是合法的。 (C17标准5.1.2.2.1(2)).
有些人可能同样会觉得在main
中修改argc
和argv
的值会造成混淆,并建议您将修改后的值赋值给一些其他变量代替:
int remaining_argc = argc - optind;
char **remaining_argv = argv + optind;
在以下程序中:
#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。所以,请耐心等待。这里什么都没有:
定义 main 以接受命令行 (cl) 参数。这需要两个参数:
- 整数 argc,它将包含 cl 参数的数量,包括程序名称,在本例中为 7
- 字符串数组(即char指针数组),其中的每个元素将指向作为cl参数传入的每个字符串文字(存储在CONSTANT内存块中)的第一个元素的内存地址程序。
for 循环(不言自明)
在 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”之后没有找到任何其他内容。
- 在每次这样的传递中
我们现在将
argc
的optind
的值“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] 指向“凤尾鱼”然后我们根据
thick
、delivery
的值打印内容,并循环遍历剩余的非选项参数以打印它们...
是的,你的理解是正确的。
我会提供两个注意事项:
“字符串文字”是指在您的程序中实际定义的字符串,作为
""
定界符之间的字符。 “CONSTANT 块”不是标准概念,但我想你的意思是从二进制文件加载的 read-only 内存块,因为这是字符串文字通常所在的位置。argv
指针指向的字符串不是这种类型;它们不可能,因为在创建二进制文件时它们是未知的。相反,它们位于某个未指定的内存区域,如果您愿意,您可以就地修改它们(尽管这可能会使您的代码混乱);例如argv[0][3] = 'x'
是合法的。 (C17标准5.1.2.2.1(2)).有些人可能同样会觉得在
main
中修改argc
和argv
的值会造成混淆,并建议您将修改后的值赋值给一些其他变量代替:
int remaining_argc = argc - optind;
char **remaining_argv = argv + optind;