O.J
O.J

Reputation: 13

getting undefined behavior in counting characters of text file program?

I wrote a c program meant to count the characters in a certain file.

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;
}

I want to add a char array into it but for some reason it changes the value of my int type (count). for example, if I run this program I get an output of 3549. Now, lets say I declare "char potato[5000]" alongside my other char type. For some reason I get a completely different output of 159062601. Why is this and how do I prevent that?

Upvotes: 1

Views: 148

Answers (2)

AdamF
AdamF

Reputation: 2940

You have several problems in your code. i will list them below:

  1. In c programming we declare variables in the scope begin. and initialize them if we need so. you have a mixture of declerations and code.
  2. count variable non initialized!! you have entered the while loop with garbage value in count. UB (Undefined behavior) - in each run you will get different values.
  3. you didnt check the return value of fopen !! you must check if the operating system succed in opening the file you have requested to manipulate.
  4. regarding asking a question in stackoverflow, your code is not complete and you didnt post all of it.

Now lets try to learn new topics regarding working with IO streams.

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.

This is how check if errors ocured while working with the file we are reading:

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.

And in this function bellow we get a human readble error, not just an errnor number!

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;
}

Since the const char *explain_ferror(FILE *fp); is not GNU standard function, i am posting a GNU standard functions in the code snippet below:

char *strerror(int errnum);

strerror is standard library c function which returns a pointer to a string that describes the error code passed in the argument errnum. Be aware that this function is not Thread safe. for thread safe function use The strerror_r().

Return Value

The strerror(), function return the appropriate error description string, or an "Unknown error nnn" message if the error number is unknown.

Since POSIX.1-2001 and POSIX.1-2008 requires that a successful call to strerror() shall leave errno unchanged, and note that, since no function return value is reserved to indicate an error, if we wishe to check for errors we should initialize errno to zero before the call (by calling void clearerr(FILE *stream);, and then check errno after the call.

#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);
}  

...

Finally: man pages (or man7) or typing man <enter_string_here> in terminal on linux shall clear all the q.marks. for further reading go to:

explain_ferror

ferror

fscanf

Upvotes: 3

user3629249
user3629249

Reputation: 16540

The following proposed code:

  1. initializes variables before using them (your compiler should have told you about this problem.
  2. properly checks and handles I/O errors for fopen() and for fscanf()
  3. properly closes the open file before exiting. I.E. it cleans up after itself
  4. properly terminates printed text, so it is immediately passed to the terminal

and now, the proposed code:

#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;
}

Upvotes: 4

Related Questions