【问题标题】:Newbie C Array question over functions关于函数的新手 C 数组问题
【发布时间】:2010-01-26 17:23:36
【问题描述】:

我买了一本名为“The C (ANSI C) PROGRAMMING LANGUAGE”的 C 书来尝试自学 C 语言。无论如何,这本书包含很多示例和实践,可以在各个章节中遵循,这很好。

无论如何,下面的代码是我对“计算最长行类型的程序”一书的回答,作者在函数getLine(char s[], int lim) 中使用了for循环。这允许在main() 函数内正确显示字符串line。但是使用 while 将不起作用 - 出于我未知的原因,也许有人可能会阐明我的错误是什么。

编辑:总结以上内容。 printf("%s\n", line); 不会显示任何内容。

感谢您的帮助。

#include <stdio.h>
#define MAXLINE 1024

getLine(char s[], int lim) {
    int c, i = 0;

    while((c = getchar()) != EOF && c != '\n' && i < lim) {
        s[++i] = c;
    }

    if(c == '\n' && i != 0) {
        s[++i] = c;
        s[++i] = '\0';
    }    
    return i;
}  

main(void) {
    int max = 0, len;
    char line[MAXLINE], longest[MAXLINE];

    while((len = getLine(line,MAXLINE)) > 0) {
        if(len > max) {
            max = len;
            printf("%s\n", line);
        }
    }
    return 0;
}

【问题讨论】:

  • 请解释它以何种方式不起作用 - 无法编译、根本无法运行、产生不正确的结果或失败并带有错误代码,请在您的问题中包含示例输出和错误输出.
  • 没有真正看过,但我猜这是 \n 与 \p 的问题?
  • 如果你在没有\n的情况下点击EOF,你还需要用\0终止你的字符串,否则如果你的文件没有以@结尾,你的程序将出现故障或崩溃987654328@
  • 您的代码无法正常工作,因为存在问题。如果您想要更具体的答案,请提出更具体的问题。

标签: c arrays function


【解决方案1】:

您有许多严重的错误。以下是我发现的问题以及如何修复它们。

将您的代码更改为后增量 i 以避免使第一个数组成员未初始化,并避免重复打印最后一个字符:

s[++i] = c;
...
s[++i] = c;
s[++i] = '\0';

s[i++] = c;
...
// s[++i] = c; see below
...
s[i++] = '\0'; 

并修复您的 EOF 错误:

if(c == '\n' && i != 0) {
    s[++i] = c;
    s[++i] = '\0';
}

if(c == '\n')
{
    s[i++] = '\n';
}
s[i] = '\0'

理论

在编写处理字符串、数组或其他向量类型结构的程序时,检查程序的逻辑至关重要。您应该手动执行此操作,并通过它运行一些示例案例,为您的程序提供示例输入并思考会发生什么。

你需要经历的案例有:

  • 几个一般案例
  • 所有边缘情况

在这种情况下,您的边缘情况是:

  • 第一个字符是 EOF
  • 第一个字符是“x”,第二个字符是 EOF
  • 第一个字符是'\n',第二个字符是EOF
  • 第一个字符是'x',第二个字符是'\n',第三个字符是EOF

  • 一行有 lim 个字符

  • 一行的字符数比 lim 少一个
  • 一行的字符数多于 lim 个字符

示例边缘案例

第一个字符是'x',第二个字符是'\n',第三个字符是EOF

getLine(line[MAXLINE],MAXLINE])
(s := line[MAXLINE] = '!!!!!!!!!!!!!!!!!!!!!!!!...'
c := undef, i := 0
while(...)
c := 'x'
i := 1
s[1] := 'x' => s == '!x!!!!...' <- first bug found
while(...)
c := '\n'
end of while(...)
if (...)
(c== '\n' (T) && i != 0 (T)) = T
i := i + 1 = 2
s[2] = '\n' => s == '!x\n!!!!'
i := i + 1 = 3
s[3] = '\0' => s == '!x\n\0!!!' <- good, it's terminated
return i = 3
(len = i = 3) > 0) = T (the while eval)
if (...)
len (i = 3) > max = F
max = 3 <- does this make sense?  is '!x\n' a line 3 chars long?  perhaps.  why did we keep the '\n' character? this is likely to be another bug.
printf("%s\n", line) <- oh, we are adding ANOTHER \n character?  it was definitely a bug.
outputs "!x\n\n\0" <- oh, I expected it to print "x\n".  we know why it didn't.
while(...)
getLine(...)
(s := line[MAXLINE] = '!x\n\0!!!!!!!!!!!!!!!!!!!...' ; <- oh, that's fun.
c := undef, i := 0
while(...)
c := EOF
while terminates without executing body
(c == '\n' && i != 0) = F
if body not executed
return i = 0
(len = i = 0 > 0) = F
while terminates
program stops.

所以您会看到这个简单的过程,它可以在您的脑海中完成,或者(最好)在纸上完成,可以在几分钟内向您展示您的程序是否可以运行。

通过了解其他边缘情况和一些一般情况,您会发现程序中的其他问题。

【讨论】:

  • 请注意,此代码将在 i 中计入 \n。你可以自己解决。
  • 感谢您的帮助(和耐心),非常感谢。
  • 感谢您在上面的编辑 - 这是非常有帮助和很好的做法 - 但是这只是第一章,还没有深入探讨这个主题。
  • 然而——作为一个建议——即使是在书中写这些练习应用程序时。你建议写“极端情况”吗?
  • 我不知道你所说的极端边缘是什么意思......根据定义,边缘不是极端吗?还是您的意思是非常不可能的边缘情况?我列出的边缘情况都不太可能。
【解决方案2】:

从您的问题中不清楚您在使用 getLine 时究竟遇到了什么问题(编译错误?运行时错误?),但您的实现中有几个错误。而不是

s[++i] = something;

您应该使用后缀运算符:

s[i++] = something;

不同的是,第一个版本在 (i+1) 的索引处存储了一些东西,而第二个版本将在 i 的索引处存储了一些东西。在 C/C++ 中,数组从 0 开始索引,因此您需要确保它在第一次通过 while 循环时将字符存储在 s[0] 中,在第二次通过时将字符存储在 s[1] 中,依此类推。使用您发布的代码,s[0] 永远不会分配给,这将导致 printf() 打印出未初始化的数据。

以下 getline 实现对我有用:

int getLine(char s[], int lim) {

    int c;
    int i;

    i = 0;

    while((c = getchar()) != EOF && c != '\n' && i < lim) {
        s[i++] = c;
    }

    if(c == '\n' && i != 0) {
        s[i++] = c;
        s[i++] = '\0';
    }

    return i;
}

【讨论】:

  • 你的版本仍然会爆炸。为什么?因为最后一个输入行不是空终止的。证明:c == EOF && i = 0 => !(c=='\n' && i!=0) => s[i-1] != \0。 c == EOF && i != 0 => c != '\n' => !(c=='\n' && i!=0) => s[i-1] != \0。砰。
【解决方案3】:

通过执行 ++i 而不是 i++,您不会在 getLine() 中为 s[0] 分配任何内容!

此外,在循环结束时分配 '\0' 时,您会不必要地递增,顺便说一句,您应该始终分配,所以从条件中取出它。

【讨论】:

    【解决方案4】:

    还将返回类型添加到函数(int main 和 int getLine)

    【讨论】:

      【解决方案5】:

      还要注意溢出 - 您在最后分配给 s[i],限制为 i == lim,因此您可能分配给 s[MAXLINE]。这将是一个 - 等待它 - 堆栈溢出,是的。

      【讨论】:

        猜你喜欢
        • 2022-12-03
        • 2010-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-01
        • 2011-02-17
        相关资源
        最近更新 更多