-3

I am looking for fast (for performance-critical code), safe and cross-platform way to check if FILE* in fact points to a file upon successful previous call to fopen().

Asking for current position with ftell() is one approach, but I doubt that it is fastest, accurate, safe or that there is no better more straightforward and dedicated for this way.

  • 3
    `if (file_pointer == NULL)` is the standard test. Post what you have tried to add clarity to your question. – chux - Reinstate Monica Apr 17 '18 at 13:48
  • In general there is no portable way to check if a pointer is valid. Not limited to file pointers. – Bo Persson Apr 17 '18 at 13:48
  • @chux that checks if the pointer is `NULL`, but it could be very well pointing to something. Imagine it is a `void* ` that can be denoted to a string literal or a `FILE* ` in accordance to the user. How do you check if it is a valid `FILE* `? Thanks for the downvotes. – JuanPabloJuliosEstebaFiores Apr 17 '18 at 13:52
  • Could use `if ((void *)some_object_pointer_from_somewhere == (void *) pointer_returned_from_previous_fopen_call)` – chux - Reinstate Monica Apr 17 '18 at 13:58
  • 3
    @JuanPabloJuliosEstebaFiores The downvotes are because the question makes no sense. `FILE *` pointers don't exist in a vacuum, they exist in programs which can be well or poorly written. In a well-written program, any `FILE *` variable contains either (a) the NULL value it was initialized to by its programmer or (b) the return value from a `fopen` call that was checked, meaning that by the time we get here we know it's non-null. But no, for a random `FILE *` pointer there's no way to know if it's valid. – Steve Summit Apr 17 '18 at 14:30
  • *Asking for current position with ftell() is one approach* Not one that will work. If the value you're testing is invalid, calling `ftell()` on it is undefined behavior. The real problem you're facing is the code you're working on has lost track of what's going on. That's what needs to be fixed. Figuring out what part of the shell you have in hand after you drop an egg on the floor doesn't make the egg whole - it's still a sloppy mess all over the floor. – Andrew Henle Apr 17 '18 at 14:55

3 Answers3

1

In C there are three kinds of pointer values:

  1. Values that are NULL, because the programmer initialized them (or because they took advantage of default static initialization).
  2. Values that were returned by a pointer-returning function such as fopen or malloc (and that have not yet been passed to fclose or free).
  3. Values where neither 1 nor 2 is true.

And the simple fact is that if you have a pointer of kind 3, there is no mechanism in the language that will tell you whether the pointer is valid or not. If you have a pointer p that might have been obtained from malloc or not, but you can't remember, there is no way to ask the compiler or run-time system to tell you if it currently points to valid memory. If you have a FILE pointer fp that might have been obtained from fopen or not, but you can't remember, there is no way to ask the compiler or run-time system to tell you if it currently "points to" a valid file.

So it's up to you, the programmer, to keep track of pointer values, and to use programming practices which help you determine whether pointer values are valid or not.

Those ways include the following:

  1. Always initialize pointer variables, either to NULL, or to point to something valid.
  2. When you call a function that returns a pointer, such as fopen or malloc, always test the return value to see if it's NULL, and if it is, return early or print an error message or whatever is appropriate.
  3. When you're finished with a dynamically-allocated pointer, and you release it by calling fclose or free or the equivalent, always set it back to NULL.

If you do these three things religiously, then you can test to see if a pointer is valid by doing

if(p != NULL)

or

if(p)

Similarly, and again if you do those things religiously, you can test to see if a pointer is invalid by doing

if(p == NULL)

or

if(!p)

But those tests work reliably only if you have performed steps 1 and 3 religiously. If you haven't, it's possible -- and quite likely -- for various pointer values to be non-NULL but invalid.


The above is one strategy. I should point out that steps 1 and 3 are not strictly necessary. The other strategy is to apply step 2 religiously, and to never keep around -- never attempt to use -- a pointer that might be null. If functions like fopen or malloc return NULL, you either exit the program immediately, or return immediately from whatever function you're in, typically with a failure code that tells your caller you couldn't do your job (because you couldn't open the file you needed, or you couldn't allocate the memory you needed). In a program that applies rule 2 religiously, you don't even need to test pointers for validity, because all pointer values in such programs are valid. (Well, all pointers are valid as long as Rule 2 was applied religiously. If you forget to apply Rule 2 even once, things can begin to break down.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • +1 Though, I know those things already. The thing is that I allow the user to add any kind of data to this `void*` I made it to detect if it is a read-only string that starts with `@` and if it is, it opens a file based on that name and assigns the FILE* fp to this void*. It is important that it is highly optimized in terms of memory and performance so I don't want to spare another void* specially for files. This also ruins my idea to have as little function args as possible – JuanPabloJuliosEstebaFiores Apr 17 '18 at 15:53
  • It is sad that people here consider this question "senseless", when I put a void* pointing to something else in `ftell()` it returns -1 as an error. If it is defined by the standards that ftell doesn't tolerate this, then sure okay, it is undefined behavior, but no one really cited this from the standards or the man page. – JuanPabloJuliosEstebaFiores Apr 17 '18 at 15:55
  • @JuanPabloJuliosEstebaFiores In general, no function in C can tolerate being handed an invalid pointer -- because in general, there's no way it can handle it gracefully, because there's no way it can determine if it's valid or not, other than trying to use it as if it's valid -- which then fails in arbitrary ways (i.e. is undefined behavior) if it's invalid. – Steve Summit Apr 17 '18 at 16:00
  • @JuanPabloJuliosEstebaFiores I doubt your performance needs are so extreme that you truly need to resort to this kind of ill-defined hackery, and even if performance does matter, there's probably a way of doing this (whatever it is -- I confess it's still quite unclear to me) that's both clean and efficient, but those are questions for another day. – Steve Summit Apr 17 '18 at 16:03
  • I wouldn't use the term "invalid pointer" because the pointer is always a pointer to a valid memory area. If the user passes invalid pointer it is his fault according to the general software policy. Also it segfaults if I pass a pointer that is not to a read-only memory area. – JuanPabloJuliosEstebaFiores Apr 17 '18 at 16:20
  • I also have a similar req, I will have a void* and need to check if this is a valid FILE* – Ram Jul 12 '22 at 08:14
  • @Ram As the rest of the comments and answers here explain, there is simply no good, portable, or guaranteed way to do that. – Steve Summit Jul 12 '22 at 11:30
1

If a call to fopen has succeeded, but you want to know whether you've just opened a file or something else, I know of two general approaches:

  1. Use fstat on the file descriptor (or stat on the same pathname you just opened), then inspect the mode bits.

  2. Attempt to seek on the file descriptor. If this works as expected it's probably a file; if it doesn't it's a pipe or a socket or something like that.

The code for (1) might look like

struct stat st;
fstat(fileno(fp), &st);
if(st.st_mode & S_IFMT) == S_IFREG)
    /* it's a regular file */

To perform (2) I normally seek to offset 1, then test to see what offset I'm at. If I'm at 1, it's a seekable file, and I rewind to 0 for the rest of the program. But if I'm still at 0, it's not a seekable file. (And of course I do this once, right after I open the file, and record the result in my own flag associated with the open file, so the performance hit is minimal.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
0

Trying to programmatically detect an invalid pointer is a little like hunting for witches.

Supposedly, one way to detect a witch was to hold her underwater. If she died, she was an ordinary human. But if she used her magical powers to avoid drowning, that meant she was a witch — so you killed her. (I can't remember just now if this was ever considered a "legitimate" method, or just a joke out of Monty Python and the Holy Grail.)

But, similarly, if you have a pointer that might be valid or might be invalid, and you try to test it by calling a function that acts on the pointer — like calling ftell on an unknown FILE pointer — there are two possible outcomes:

  1. If the pointer was valid, the function will return normally.
  2. But if the pointer was invalid, the behavior is undefined. In particular, it's significantly likely that the program will crash. That is, the function will not return normally, and it will not return with an error code, either. It will not return at all, because the program will have crashed, and your code (that was going to do one thing or the other depending on whether the pointer was or wasn't valid) won't run at all, because the whole program won't be running any more.

So, once again, if a pointer might or might not be valid, you (that is, explicit code in your program) must keep track of this fact somehow. If you have an unknown pointer value, for which you've lost track of its status, there is no well-defined way to determine its validity.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103