在计算文本文件程序的字符时出现未定义的行为?

getting undefined behavior in counting characters of text file program?

我写了一个 c 程序来计算某个文件中的字符数。

int main(void) {
    FILE *fp;
    fp = fopen("txt.txt", "r");
    char text;
    int count;
    while (fscanf(fp, "%c", &text) != EOF) {
        count++;
    }
    printf("%d", count);
    return 0;
}

我想向其中添加一个 char 数组,但由于某种原因,它更改了我的 int 类型(计数)的值。 例如,如果我 运行 这个程序,我得到 3549 的输出。现在,假设我在其他字符类型旁边声明了“char potato[5000]”。出于某种原因,我得到了完全不同的输出 159062601。这是为什么,我该如何防止?

建议代码如下:

  1. 在使用变量之前初始化变量(你的编译器应该已经告诉你这个问题了。
  2. 正确检查和处理 fopen()fscanf()
  3. 的 I/O 错误
  4. 退出前正确关闭打开的文件。 IE。它会自行清理
  5. 正确终止打印的文本,因此它会立即传递到终端

现在,建议的代码:

#include <stdio.h>
#include <stdlib.h>


int main(void) 
{
    FILE *fp;
    fp = fopen("txt.txt", "r");
    if( ! fp )
    {
        perror( "fopen failed" );
        exit( EXIT_FAILURE );
    }
    
    char text;
    int count = 0;

    while ( fscanf( fp, "%c", &text ) == 1 ) 
    {
        count++;
    }

    fclose( fp );

    printf( "%d\n", count );
    return 0;
}

您的代码中有几个问题。我将在下面列出它们:

  1. c 编程中,我们在范围begin 中声明变量。并在需要时初始化它们。你混合了 declerations 和代码。
  2. count 变量未初始化!!您已进入 while 循环,其中计数为垃圾值。 UB(未定义行为)- 在每个 运行 中你会得到不同的值。
  3. 你没有检查 fopen 的 return 值!!您必须检查操作系统是否成功打开了您请求操作的文件。
  4. 关于在 Whosebug 中提问,您的代码不完整,您没有post全部。

现在让我们尝试学习有关使用 IO 流的新主题。

return value of function fscanf

The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs. EOF is also returned if a read error occurs, in which case the error indicator for the stream (see ferror(3)) is set, and errno is set indicate the error.

这是检查在处理我们正在阅读的文件时是否发生错误的方法:

int ferror(FILE *stream);

The function ferror() tests the error indicator for the stream pointed to by stream, returning nonzero if it is set. The error indicator can only be reset by the clearerr() function.

在下面的这个函数中,我们得到了一个人类可读的错误,而不仅仅是一个错误编号!

explain_ferror

const char *explain_ferror(FILE *fp);

The explain_ferror function is used to obtain an explanation of an error returned by the ferror(3) system call. The least the message will contain is the value of strerror(errno), but usually it will do much better, and indicate the underlying cause in more detail.

The errno global variable will be used to obtain the error value to be decoded.


#include <stdlib.h>
#include <stdio.h>
#include <libexplain/ferror.h>   /* for the non standard const char* explain_ferror(FILE* fp); */

int main(void) 
{
    FILE *fp;
    char text;
    int count = 0;
    
    fp = fopen("txt.txt", "r");
    if(fp == NULL)
    {
        perror("fopen failed");  /*write to standard error*/ 
        exit(EXIT_FAILURE);
    }

    while (fscanf(fp, "%c", &text) != EOF) 
    {
        ++count;
    }
    
    if (ferror(fp))  /* nonzero return if error occured */
    {
        fprintf(stderr, "%s\n", explain_ferror(fp));
        exit(EXIT_FAILURE);
    }  
    
    printf("%d", count);
    return 0;
}

由于 const char *explain_ferror(FILE *fp); 不是 GNU 标准函数,我在下面的代码片段中 posting GNU 标准函数:

char *strerror(int errnum);

strerror 是标准库 c 函数,它 return 是一个指向字符串的指针,该字符串描述参数 errnum 中传递的错误代码。请注意,此函数不是 Thread safe。对于线程安全函数,使用 strerror_r().

Return值

strerror() 函数 return 适当的错误描述字符串,如果错误号未知,则显示“未知错误 nnn”消息。

由于 POSIX.1-2001POSIX.1-2008 要求成功调用 strerror() 应保持 errno 不变,请注意,因为没有保留函数 return 值为了指示错误,如果我们希望检查错误,我们应该在调用之前将 errno 初始化为零(通过调用 void clearerr(FILE *stream);,然后在调用之后检查 errno。

#include <string.h>
#include <errno.h>
#include <stdio.h>  

...

clearerr(fp);  /* clear previous seted errno */

while (fscanf(fp, "%c", &text) != EOF) 
{
    ++count;
}
    
if (ferror(fp))  /* nonzero return if error occured */
{
    fprintf(stderr, "%s\n", strerror(errno));
    exit(EXIT_FAILURE);
}  

...

最后: 手册页(或 man7)或在 linux 的终端中键入 man <enter_string_here> 应清除所有 q.marks。 如需进一步阅读,请访问:

explain_ferror

ferror

fscanf