(*++argv)[0] == ‘-‘
shuiming….. (*++argv)[0] == ‘-‘:what - means?
cc….. It's the dash character, the hyphen: '-' is a character constant whose value is 45 in ASCII. The test asks: "does this argument start with a dash?"
That's the Unix convention for options. In ls -l /home, the -l starts with a dash, so it's an option (a switch that changes behavior), while /home has no dash, so it's ordinary input. K&R's loop keeps going while arguments start with -, handling each as an option, and stops at the first one that doesn't, which is where the real data begins.
So for ./c-c -df -n hello: -df passes, -n passes, hello fails the test and the loop ends.
shuiming….. ‘-‘写的时候他们心里想着的是gcc之后的 ./c-c -def -n hello 写代码的时候就考虑着terminal 里的运行
cc….. 对,你看到要害了。写代码时,心里先有的是终端里那一行命令:用户会怎么敲、哪些是选项、哪些是数据。代码是从那一行倒推出来的。
这正是Unix那一代人的习惯。Kernighan、Ritchie、Thompson自己天天坐在终端前用这些程序,写的人就是用的人。所以他们先定好约定:选项以-开头,可以合在一起写成-df,选项在前,数据在后。然后写main去接住这个约定。
换句话说,'-'这个字符不是代码内部的东西,而是代码和终端之间的一份契约。你读每个K&R程序都可以先问一句:它在终端里会被怎么调用?想清楚这一行,函数里的细节就有了方向。这也是你说的庙算:先在心里跑一遍,再动手。
shuiming….. argv argc 也是他们预设的
cc….. 对,但要分两层看。
名字是约定俗成的。argc、argv只是K&R起的名字,你写成int main(int n, char *words[])照样能跑,编译器不在乎。大家都用argc/argv,是因为K&R这么写,五十年下来成了习惯。
真正预设的是形状:第一个参数是int,表示有几个词;第二个是指向字符串的指针数组,最后以NULL结尾。这个形状是Unix和C的设计者定下的,后来写进了C标准,操作系统和编译器都照着这个形状把命令行交给main。
所以名字可以换,形状不能换。就像周易的卦名你可以另起,但六爻的位置和0/1的结构是定死的。
shuiming….. 他得给人讲清楚啊?
cc….. 其实K&R讲了,在第5.10节“命令行参数”。那一节开头就说:main被调用时带两个参数,第一个习惯上叫argc,是参数个数;第二个叫argv,是指向字符串数组的指针。紧接着用echo程序演示,还画了一张和cc那张很像的图。
问题在于他们讲得太省。一段话、一个例子,就往下走了。1978年和1988年的读者大多本来就坐在Unix终端前,天天敲命令,不需要多说。对他们,argc/argv是顺理成章的事;对今天从VS Code进来的人,这一步就跳得太快。
这也是读原典的代价:作者不替你补背景。好处是没有废话,坏处是每一句都可能藏着一整块默认知识。你现在补上的,正是那一块。回头再读5.10节,应该会觉得每句话都落地了。
shuiming….. 真的弄死人,会出人命的
cc….. 哈哈,这种卡法确实折磨人:盯着几个符号看半天,原来答案根本不在代码里,在终端那一边。
不过卡一回,通一片。argc/argv这一关过了,后面第7章的输入输出、第8章的系统调用,都是同一条线:程序和外面的世界怎么对接。你现在已经站在那条线上了。
慢慢来,命保住,复利才算得下去。
shuiming….. - -argc/(++argv)[0]/++argv[0] 这更是把人向火坑里推
cc….. 这三个确实是K&R里最压缩的写法,一行里同时做了“移动、取值、比较”三件事。读的人得在脑子里同时转三个指针,不掉坑才怪。
好消息是,同样的意思可以写得平白很多:
for (int i = 1; i < argc && argv[i][0] == '-'; i++)
for (int j = 1; argv[i][j] != '\0'; j++)
switch (argv[i][j]) {
case 'n': numeric = 1; break;
case 'r': reverse = 1; break;
}
i管第几个词,j管词里第几个字母,argv[i][j]就是那个字母。没有--,没有++,没有括号优先级,指针一个都不动。效果和K&R那两行完全一样。
K&R那种写法是七八十年代的手艺习惯,那时候屏幕小、终端慢,大家以短为美。要会读它,因为老代码里到处都是;但自己写的时候,用下标版就好。先读懂下标版,再回头对照指针版,一一对应:i就是argv移动了几次,j就是argv[0]移动了几次。这样火坑就成了台阶。
shuiming….. 这样坚冰有点松动
cc….. 坚冰松动,春水就快来了。
这一步要紧的是那个对应关系:下标是“走了几步”,指针是“走到哪儿了”,两种写法说的是同一件事。以后再碰到K&R里*p++这类写法,都可以先翻成下标版,看懂了再翻回去。翻得多了,指针写法自己就会顺眼起来。
shuiming….. 以前看上去是天上落下的仙女,没想到只是一个下层农家的小媳妇
cc….. 哈哈,这个比喻妙。远看云里雾里,凑近一看,就是个踏实干活的人:数一数有几个词,挨个看是不是以-开头,再挨个读字母。全是农家活计,一点仙气也没有。
C的大部分东西都是这样,看着玄,拆开都是搬砖:地址加一,取个值,比个大小。你在仓库里一件一件stow,它在内存里一格一格挪,本质上是同一种手艺。