C fopen() fclose() pointers don't match

Greetings,

First timer posting so please forgive some of the code posting prettiness I haven't figured out yet...

Running FreeBSD since the old Dr.Dobbs articles and just now trying to get back into some C programing and some other scripting. Learned K&R C back in 1986 or so and did data acquisition in the 90s. Basicly been a JOAT for the last 20 years and am trying to get back into programming and ran across this little gem of a problem.

System - FreeBSD on MS HyperV and VMWare Player - FreeBSD version 14.3 Release P7.

So, my problem is with fopen() and fclose(); man 3 says that fopen() returns a FILE * and fclose() requires FILE * parameter.

This is not correct. Function fopen() returns type int while function fclose() requires parameter FILE *.

So, WHAT IS UP with this?

Here is the screen copy of code and compiler -- cc -- results

file handle = FILE *
LISTING 1 ----------
root$ cat test.c
#include <stdio.h>
#include <stdlib.h>
/*-------------------------------------------*/
int main(int argc, char *argv[])
{
FILE *fptr;

if (argc != 2)
{
printf("Error: test filename \n");
exit(-1);
}
printf("%s\n",argv[1]);
if ((fptr = fopen(argv[1],"r+") == NULL))
{
printf("test: %s: No such file\n", argv[1]);
exit(-1);
}
fclose(fptr);
exit(0);
}

root$ cc test.c
test.c:14:13: error: incompatible integer to pointer conversion assigning to 'FILE *' (aka 'struct __sFILE *') from 'int' [-Wint-conversion]
14 | if ((fptr = fopen(argv[1],"r+") == NULL))
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
root$

file handle = int
LISTING 2 -----------------------

root$ cat test.c
#include <stdio.h>
#include <stdlib.h>
/*-------------------------------------------*/
int main(int argc, char *argv[])
{
int fptr;

if (argc != 2)
{
printf("Error: test filename \n");
exit(-1);
}
printf("%s\n",argv[1]);
if ((fptr = fopen(argv[1],"r+") == NULL))
{
printf("test: %s: No such file\n", argv[1]);
exit(-1);
}
fclose(fptr);
exit(0);
}
root$ cc test.c
test.c:19:10: error: incompatible integer to pointer conversion passing 'int' to parameter of type 'FILE *' (aka 'struct __sFILE *') [-Wint-conversion]
19 | fclose(fptr);
| ^~~~
/usr/include/stdio.h:251:19: note: passing argument to parameter here
251 | int fclose(FILE *);
| ^
1 error generated.
root$


END-
 
if ((fptr = fopen(argv[1],"r+") == NULL))
you have the parentheses wrong here
and fopen returns indeed FILE*

you assign fptr with the logical value of the fopen() == NULL
use ((fptr = fopen()) == NULL) or split it in 2 lines
fptr = fopen()
if (fptr == NULL) ...
Good catch. Sometimes it's easier to debug when you write things "the long way" (multiple statements, etc).

open()/close() work on file descriptors (ints) while fopen() / fclose() work on FILE* (namin convention in the libraries)
 
I prefer
C:
if (NULL == (fptr = fopen(argv[1],"r+")))
or (more paranoid)
C:
if ((FILE *)NULL == (fptr = fopen(argv[1],"r+")))
to fool-proofing for cases missingly deleted one "=" in "==".

With this style (told by my friend decades ago), when missingly deleted single "=" mentioned above, the comparison becomes "attempt to set value into immutable constant / literal" that is clear syntax error, thus, compiler can error out.
 
This thread is a good example of how to do a code review :)
T-Aoki I've seen that coding style a lot; it takes a little getting used to but I think the compiler error messages may be better.
Also compiler flags to treat warnings as errors should be the default at least in my opinion
 
Instead of comparing with NULL, I use ! - for example if (!(f = fopen(argv[1], "r+"))) - but I am sure this is frowned upon these days by the code style police.
 
C:
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(int argc, char *argv[]) {
  int rc=-1;

  if (2 != argc) {
    fprintf(stderr, "Error: test <filename>\n");
  } else {
    FILE *fptr;

    fptr = fopen(argv[1], "r+");
    if (NULL != fptr) {
      printf ("opened file: %s\n", argv[1]);
      fclose (fptr);
      // strictly we should check the fclose return code here
      printf ("closed file: %s\n", argv[1]);
      rc = 0;
    } else {
      fprintf(stderr, "failed to open file: %s - %s\n",
          argv[1],
          strerror(errno));
    }
  }

  return rc;
}
Try my version.

I assumed you used the fopen filemode "r+" intentionally, rather than "r"; note that this means that if you say something like
$ ./test test
you will get an error saying you cannot open a running progam for "r+", although clearly the file exists.

Arguably printing that the program closed the file is redundant here, too, but is included for completeness.
 
Following Cracauer's suggestion we can further contract the code by substituting for if-else statements using the boolean logical operators and the comma operator, as follows:-
C:
#include <stdio.h>
#include <string.h>
#include <errno.h>
 
int main(int argc, char *argv[]) {
  int rc=-1;
  FILE *fptr;
 
  2 != argc && (fprintf(stderr, "Error: test <filename>\n")) ||
  (fptr = fopen(argv[1], "r+")) && (printf ("opened file: %s\n", argv[1]), fclose (fptr), rc=0) ||
      NULL==fptr && fprintf(stderr, "failed to open file: %s - %s\n", argv[1], strerror(errno));
 
  return rc;
}
Of course this is considerably less readable than the earlier version. Which means it's easier to screw it up when you're writing it, too. There is no advantage to writing code this way, unless you are trying to make it less readable! Nonetheless, it's kind of interesting.
 
The boolean chain instead of if thing doesn't really translate to languages that use integer like types for bools. In Lisp you can use boolean expressions on any data type. NIL is false, evertthing else is true.

Code:
(defun mystream ()
  (or (open-file "file1")
      (open-file "file2")
      *standard-output*))
 
Yes, C doesn't lend itself to that type of structure, not very well, anyway. It's the kind of thing people play around with in obfuscated C contests. It fits naturally in Lisp.
 
When I learned Lisp there was no if.
It was a subset on Personal Computer.
I learned Lisp using and, or, cond, etc.

Well, Lisp is the programmable programming language.

If you don't have "if" in Lisp it is only a macro away.

If you don't have "if" in a normal language you go have to hack up the compiler and change the standard.
 
Actually (rval == lval) "(null == x)" is a good way to trigger compiler errors if mistakenly using assignment operator. It is "safe coding" at its finest.
There is compiler warning for this. It is stupid trend blindly followed by many people. Most stupid is to compare "if ( 5 < a )" instead of "if ( a > 5 )".
 
The thing you're trying to protect against is accidentally typing '=' instead of the intended '==' or '!=' (or '<=' or '>='), which is a well-known classic bug in C. Or a random pigeon flying in through the window and pecking at the delete key when the cursor is under the '!' or '<' or '>' character. Pesky pigeons! Why is it a problem? Because the incorrect code will in many cases compile with no warnings!

You know, it depends on how good last night's party was, and Murphy's law reliably predicts that it WILL happen. 😁 The '==' and '!=' are the most indsidious because visually the glyphs are smaller than the '>=' or '<=' operators, so it's easier to miss the mistake when reading the code. Of course the underlying root cause is that C allows you to make an assignment within a conditional expression, which was probably a "fallacy of good intentions" made by the designers of C.

Sure, '(a > 5)' is much easier to read than '(5 < a)', at least, if you speak one of the latin family of languages that read from left to right; I would never use '(5 < a)' since it doesn't solve any problem and worsens readability.

Trust me, when you're working on codebases of millions of lines of C code, those kinds of bugs are guaranteed to occur, and furthermore they will be a huge pain in the elbow to debug!
 
  • Like
Reactions: mer
Back
Top