1

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?

Adam
  • 2,820
  • 1
  • 13
  • 33
O.J
  • 13
  • 3
  • 4
    Try `int count = 0;` – jackw11111 Jun 26 '20 at 05:51
  • 2
    ⟼Remember, it's always important, *especially* when learning and asking questions on Stack Overflow, to keep your code as organized as possible. [Consistent indentation](https://en.wikipedia.org/wiki/Indentation_style) helps communicate structure and, importantly, intent, which helps us navigate quickly to the root of the problem without spending a lot of time trying to decode what's going on. – tadman Jun 26 '20 at 05:52
  • 3
    What you're describing is *undefined behaviour* and it's caused by a number of things, but most commonly it's *using uninitialized variables* like `count` here. That does not default to 0. It defaults to *whatever*, or in other words the random garbage you're seeing. – tadman Jun 26 '20 at 05:53
  • just initialize count as 0, you should be good to go – rizesky Jun 26 '20 at 05:53
  • 2
    Enable all warnings and debug info in your compiler. With [GCC](http://gcc.gnu.org/) compile `yourcode.c` with `gcc -Wall -Wextra -g yourcode.c -o yourprog`. Learn to use a debugger such as [GDB](https://www.gnu.org/software/gdb/) – Basile Starynkevitch Jun 26 '20 at 05:54
  • 3
    It's worth nothing that this mystery could have been solved in seconds by stepping through in a debugger where it would be obvious that `count` has a garbage value before the first iteration. This is why learning to use a debugger is an essential skill when working with C code. – tadman Jun 26 '20 at 05:54
  • 3
    Read [*What every C programmer should know about undefined behavior*](http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html) – Basile Starynkevitch Jun 26 '20 at 05:56
  • the posted code does not compile! Amongst other things, it is missing the statement: `#include ` – user3629249 Jun 26 '20 at 06:08
  • 2
    regarding: `fp = fopen("txt.txt", "r");` ALWAYS follow a call to `fopen()` with `if( ! fp ) { perror( "fopen failed" ); exit( EXIT_FAILURE ); }` where `exit()` and `EXIT_FAILURE` are exposed via: `#include ` – user3629249 Jun 26 '20 at 06:10
  • This seems related https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong?r=SearchResults&s=2|248.5730 – RubberBee Jun 26 '20 at 06:24
  • 1
    On Linux, consider using [stat(2)](https://man7.org/linux/man-pages/man2/stat.2.html) since the file size is part of its metadata. – Basile Starynkevitch Jun 26 '20 at 06:36

2 Answers2

4

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;
}
user3629249
  • 16,402
  • 1
  • 16
  • 17
  • 1
    Always prefer the compare to desired result as in `fscanf( fp, "%c", &text ) == 1` as this answer does versus the compare to one undesired result as with OP's `fscanf(fp, "%c", &text) != EOF`. – chux - Reinstate Monica Jun 26 '20 at 08:51
3

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

Adam
  • 2,820
  • 1
  • 13
  • 33
  • 1
    Remember, ALL code needs to be indented at least 4 spaces, other wise it will be treated as text rather than code. failed to initialize `count` failed to add the trailing `'\n' to the format string of the call to `printf()` Other things can cause `fscanf()` to fail besides EOF, should be checking the count of successful 'input format conversion' specifiers – user3629249 Jun 26 '20 at 06:29
  • @user3629249- now i have compeleted my answer. i am slowly man! :-). thanks for the comment. – Adam Jun 26 '20 at 06:46
  • 1
    "The error indicator can only be reset by the clearerr() function." --> `rewind()`, `freopen()` also clear it. – chux - Reinstate Monica Jun 26 '20 at 08:41
  • "if a read error occurs ...errno is set indicate the error." - that is not specified by C nor the standard C lib. `explain_ferror()` is also not specified there. – chux - Reinstate Monica Jun 26 '20 at 08:42
  • `if (ferror(fp) < 0)` better as `if (ferror(fp))` since the return value is not specified to be negative but "The ferror function returns nonzero if and only if the error indicator is set for stream." – chux - Reinstate Monica Jun 26 '20 at 08:46
  • you are right its not standard. include shall be made to use it. its taken from an official website die.net and its in the lib/header . – Adam Jun 26 '20 at 08:49
  • @Olivier-John Nzia - if my answer was useful i would be glad if you accept it by pushing the (grey " V " nearby the answer begin). thanks – Adam Jun 26 '20 at 09:26