getop()函数 K&R书 P78

6

我正在学习K&R书。目前我正在阅读第78页上的getop()函数。 我理解了代码,但我需要澄清两件事。

getop()函数的代码如下:

int getch(void);
void ungetch(int);

/* getop: get next character or numeric operand */
int getop(char s[])
{
    int i, c;
    while ((s[0] = c = getch()) == ' ' || c == '\t')
        ;
    s[1] = '\0';

    if (!isdigit(c) && c != '.')
        return c; /* not a number */

    i = 0;
    if (isdigit(c)) /* collect integer part */
        while (isdigit(s[++i] = c = getch()))
            ;
    if (c == '.') /* collect fraction part */
        while (isdigit(s[++i] = c = getch()))
            ;
    s[i] = '\0';

    if (c != EOF)
        ungetch(c);

    return NUMBER;
}

我的问题是关于:s[0]在以下代码中的含义:

 while ((s[0] = c = getch()) == ' ' || c == '\t')

while ((c = getchar()) == ' ' || c == '\t') ;
while循环的想法是跳过空格和横向制表符,那么为什么我们要将'c'保存在s[0]中呢?为什么作者们不直接写如下代码:
while ((getchar()) == ' ' || c == '\t') ;
while (c= getch() == ' ' || c == '\t')

之后我们将不再使用空格和制表符,那么为什么需要将 c 保存在 s[0] 中?这里需要 s[0] 的原因是什么?

我的第二个问题是关于:

s[1] = '\0';

为什么在这里将'\0'(字符串结束符)分配给s[1]?我已经阅读了stackoverflow.com上关于它的一些先前答案,但我还不完全相信!关于上述问题的被接受答案是:“因为函数可能在剩余输入被读取之前返回,然后s需要是一个完整(并且终止的)字符串。”好的。但是如果输入以一个空格开头,然后跟着一个操作数或操作符呢?在这种情况下,s[1]='\0'会过早地关闭字符串吗?难道不是吗?
4个回答

6
作为对第一个问题的回答,这种情况下对s[0]的分配是一种方便的编码快捷方式。无论是否使用或丢弃getch()读取的每个字符的值,都会将c的值复制到s[0]中。如果要丢弃它,那没问题;下一次while()循环迭代时它会被覆盖。如果要使用它,则它已经被复制到目标数组s[]中所需的位置中。
作为对第二个问题的回答:

但是如果输入在开头有一个空格,然后跟着一个操作数或运算符呢?

请注意,前面的while()循环防止在退出循环后出现空格字符(空格和制表符)在s[0]中。因此,在执行完之后,不必担心在字符数组s[]中出现空格。
s[1] = '\0';

s[]字符串将由一个既不是空格也不是制表符的单个字符和一个字符串终止符组成。

在下一条语句中

if (!isdigit(c) && c != '.')
    return c; /* not a number */

该函数将在字符不是数字或小数点时返回。这就是为什么终止字符串是必要的。

1
好的解释,虽然我会移除一些正面形容词,比如“方便”,因为这表明这个杂乱无章的K&R代码遵循了某种良好的C编程实践,而实际上它充满了相反的东西。 - Lundin
@Lundin:我完全同意,从“良好编码实践”的角度来看,K&R代码确实很糟糕,但该代码展示了C语言的某些新颖之处,对于初学者来说非常重要。从算法设计的角度来看,我认为从代码的原始作者的角度来看(在今天的“良好编码实践”出现之前),在此时将s [0]赋值是一种方便,因为它以后不需要执行。 - sifferman

1
如果输入以一个空格开头,然后跟着一个操作数或运算符,那该怎么办?这种情况下,s[1] = '\0' 会过早地关闭字符串吗?不是这样吧?
不是的。
i = 0;
if (isdigit(c)) /* collect integer part */
    while (isdigit(s[++i] = c = getch()))

这会确保,如果有什么需要读取的内容,它将被覆盖在 \0 上,因为 i=0s[++i] 的意思是存储在包含 \0s[1] 中。

谢谢回答我的第二个问题!对我第一个问题有什么评论吗? - Hussein Barada

0

关于你的第一个问题:s[0] in:

while ((s[0] = c = getch()) == ' ' || c == '\t')

因为将 'c' 保存在 s[0] 中有助于提前存储第一个数字,这样我们就可以从简单的 i 等于 1 开始下一段代码。

i = 0;
if (isdigit(c)) /* collect integer part */
    while (isdigit(s[++i] = c = getch()))

上述代码用于存储下一个字符串字符,该字符从索引i = 1开始

关于您的第二个问题:

我们无法执行

s[0] = '\0';

因为在那个时候,我们已经将第一个数字存储在字符串 s[0] 中了。

请看。

(s[0] = c = getch())

0

这里给出的答案已经很好了,不过我想在第二个问题上再添加一个观点。
"好的。但是如果输入以一个空格开头,后面跟着一个运算符或操作符呢?在这种情况下,s[1] = '\0' 会过早地关闭字符串吗?不是吗?"
在这种情况下,我们根本不关心字符串(无论如何,如果遇到数字,字符串都会被覆盖),因为字符串仅在遇到十进制数时使用,其余字符如“+”、“-”或“\n”直接返回。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接