I noticed that I had used a char* variable instead of a FILE* variable in my code when using fopen and fgets, but my code works. I am wondering why this is? A section of my code is as follows.
...
char* filePath = ac->filepath;
char* line = malloc(sizeof(char) * MAX_CHAR_PER_LINE) ;
filePath = fopen(filePath, "r"); // we are assigning the result to a char*, not FILE*
if (filePath == NULL) {
printf ("\n[%s:%d] - error opening file '%s'", __FILE__, __LINE__, filePath);
printf ("\n\n");
exit (1);
}
while ((fgets(line, MAX_CHAR_PER_LINE, filePath) != NULL)) {
...
Both a char* and a FILE* simply store a memory address. C has fairly weak typing (Edit: this was a misunderstanding on my part, see comments below) so it lets you assign pointers without worrying about the type they point to.
fopen returns the address of a FILE object and you store that address somewhere (in your case it is in a char*). When you use the address in fgets it still has the address of the FILE object so everything will work as expected.
Your compiler is very permissive. It should have complained that the FILE* to char* conversion is invalid.
Anyway, assuming the compiler accepts this implicit conversion, the answer is quite simple.
By the C standard, a char* and a void* pointer can hold, without loss, any pointer value.
So, you can convert from SOMETYPE* to char* and then, back from char* to SOMETYPE*, and you get the same pointer you initially had.
Actually, on most systems, all pointers are equivalent and you can freely convert from one to another.
FILE* is a small and opaque value of pointer type. Probably an actual pointer to an internal data structure, but this may be implemented differently (e.g. it might be a UNIX file descriptor converted to a pointer).
The STDIO functions just expect that you use the same opaque FILE* value you got from fopen, in fread/fwrite/fclose.
The FILE* is converted to char* when assigned to filePath.
The char* is converted to FILE* as the first parameter of fgets (again, your compiler should complain), and so, gets back to its initial value.
Suggestion: Use higher levels of errors/warnings in your compiler and fix your code.
Related
So I have declared a pointer *f of type FILE and now I say that that pointer is equal to fopen("text.txt", "r"). So since a pointer stores an address, is fopen giving back the address of a file?
FILE *f;
f = fopen("text.txt","r");
is fopen giving back the adress of a file?
No. There is no such thing as "address of a file".
What fopen returns is a pointer to dynamically allocated opaque structure FILE, which describes how to get the contents of the file. This description is opaque in a sense that it provides no useful info to you. But routines such as fgets(), fread(), etc. know how to use that info to get the actual file contents.
fclose deallocates this structure, so if you have matching fopen and fclose there are no memory leaks (from these functions).
The function fopen is returning the address of an object of type FILE. According to §7.21.1 ¶2 of the ISO C11 standard, this object must be capable of
recording all the information needed to control a stream, including its file position indicator, a pointer to its associated buffer (if any), an error indicator that records whether a read/write error has occurred, and an end-of-file indicator that records whether the end of the file has been reached;
The exact size and contents of this object may differ from compiler to compiler and is of no interest to the average programmer. All that the average programmer must know is that they must pass the pointer returned by fopen to other I/O functions provided by the C standard library.
The standard does not specify how FILE should be defined. The only thing it says is what result you will get when you pass an object of that type to various functions. This means that this type may be different in various implementations. This is ONE way that is used:
typedef struct _iobuf
{
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
} FILE;
It comes from MinGW32.
Yes, it's giving back the address of a FILE object. Now that type is opaque. That is, you are meant to not know the actual contents of the data structure being referenced and hence must never dereference the pointer (happily, the compiler will not let you get away with this).
As far as I understand passing a pointer to a function essentially passes the copy of the pointer to the function in C. I have a FILE pointer that I pass to a function func(), func() reads a line from a file and then when we return to main(). I read another line from the file using the same FILE pointer.
However, while I would imagine that I'd read the line exactly from before func() was called, I actually read the next line after what func() had read. Can you please explain why FILE pointer behaves this way?
This is my code:
#include <stdio.h>
#define STR_LEN 22
void func(FILE *fd);
int main() {
FILE *fd;
char mainString[STR_LEN];
if (!(fd = fopen("inpuFile", "r"))) {
printf("Couldn't open file\n");
fprintf(stderr, "Couldn't open file\n");
}
func(fd);
fgets(mainString, STR_LEN, fd);
printf("mainString = %s\n", mainString);
fclose(fd);
return 0;
}
void func(FILE *fd) {
char funcString[STR_LEN];
fgets(funcString,STR_LEN, fd);
printf("funcString = %s\n", funcString);
}
However, while I would imagine that I'd read the line exactly from before func was called ...
I can't imagine why you would imagine that. What if the FILE* references a network connection that has no replay capability at all where reading is consumption. Where would the line be stored such that you could read it again? There would be absolutely no place to put it.
Not only would I not imagine that, it's kind of crazy.
As far as I understand passing a pointer to a function essentially passes the copy of the pointer to the function in C.
Correct. But a copy of a pointer points to the very same object. If I point to a car and you copy me, you're pointing to the very same one and only car that I'm pointing to.
Because FILE pointer points on some data that gets changed when the file is read/written.
So the pointer doesn't change (still points to the handler structure of the file) but the data pointed by the structure does.
Try passing pointer as const FILE * you'll see that you cannot because fread operation (and others) alter the pointed data.
One way would be to duplicate the file descriptor, which dup does, but doesn't work on buffered FILE object, only raw file descriptors.
The problem is in your initial statement:
As far as I understand passing a pointer to a function essentially passes the copy of the pointer to the function in C.
This does not change much, as whatever you are accessing as a pointer, still holds the location of the FILE you are accessing, the whole point of using pointers as arguments for a function in C, is so that you can modify a certain value outside the scope of a function.
For example, common usage of an integer pointer as a function argument:
void DoSomethingCool(int *error);
Now using this code to catch the error would work like this:
int error = 0;
DoSomethingCool(&error);
if(error != 0)
printf("Something really bad happened!");
In other words, the pointer will actually modify the integer error, by accessing it's location and writing to it.
An important thing to keep in mind to avoid these kinds of misunderstandings is to recognize that all a pointer is, is essentially the address of something.
So you could (in theory, by simplifying everything a lot) think of an int * as simply an int, the value of which happens to be an address of some variable, for a FILE *, you can think of it as an int, where the value of the int is the location of the FILE variable.
FILE *fd is a pointer only in the sense that its implementation uses C construct called a "pointer". It is not a pointer in the sense of representing a file position.
FILE *fd represents a handle to a file object inside the I/O library, a struct that includes the actual position of the file. In a grossly simplified way, you can think of fd as a C pointer to a file pointer.
When you pass fd around your program, I/O routines make modifications to the file position. This position is shared among all users of fd. If a func() makes a change to that position by reading some data or by calling fseek, all users of the same fd will see the updated position.
I'm stuck for sometime in this code:
#include <stdio.h>
const char *fn; int a;
int exists(const char *fname)
{
FILE *file;
if (file = fopen(fname, "r"))
{
fclose(file);
return 1;
}
return 0;
}
main(){
printf("Name:\n");
scanf("%s",&fn);
a=exists(&fn)
if(a==0){
fopen(&fn,"w");
fprintf(fn,"banhdhsjha");
}
}
When I try to run the program, it works until fprintf(fn,"banhdhsjha");, but it crashes here (Windows gives me an error) and the compiler (CodeBlocks) gives me the following notice:
passing argument 1 of 'fopen' from incompatible pointer type.
I am trying to make fprintf write data in the file, but I don't know how to do it. Can you help me?
There are quite a few problems in your code.
You are passing &fn to scanf. This &fn is a pointer to pointer of const char ** type. This does not make sense. Format specifier %s requires a const char * argument, not const char **. You are basically using the pointer fn itself as a target buffer for file name (4 or 8 bytes long, depending on your platform's pointer size). Most likely you type in longer file name than that "buffer" can accommodate. That overrides memory in your program and leads to unpredictable (undefined) behavior.
The proper form is probably scanf("%s", fn), but the problem is that you never allocated memory for the target buffer. You have to make sure that fn points to a char buffer of sufficient size to hold your file name.
Your fopen(&fn,"w") suffers from the same problem (and that is what the compiler is telling you). fopen expects an argument of const char * type and you are passing a const char ** instead. The proper form is fopen(fn,"w"), but again, see 2. Also, fopen return a file handle that you are supposed to store and use later. You are ignoring (discarding) the return value of fopen. That also makes no sense. You need an additional FILE * variable to store the return of fopen. You already know that, judging by what you did in exists function, but somehow you are ignoring that knowledge in your main.
Your fprintf call also makes no sense at all. fprintf requires file handle (of FILE * type) as its first argument. Instead, you are trying to pass it a file name. That's not going to work and that's also going to trigger a diagnostic message from the compiler. You are supposed to store the return value of fopen (as I said in 3) and pass it to fprintf.
Your exists(&fn) call suffers from the same problem as 1 and 3 and produces the same diagnostic message as your fopen call.
Stop trying to write random code. Where did you get the idea to pass file name to fprintf instead of file handle? Read the documentation for each function you are trying to use and act accordingly.
gcc 4.7.2
c89
bstrlib.h from Better String Library
I am maintaining someone's code that looks like this:
FILE *db_fp = NULL;
bstring data = NULL;
db_fp = db_open(DB_FILE, "r");
LOG_CHECK(db_fp == NULL, "Failed to open database: %s", DB_FILE);
data = bread((bNread)fread, db_fp);
LOG_CHECK(data == NULL, "Failed to read from db file: %s", DB_FILE);
db_close(db_fp);
return data;
I am having a little trouble understanding the following line:
data = bread((bNread)fread, db_fp);
What I can guess, is that it is fetching a bstream from the following file pointer and returning a bstring. However, I am wondering about bread and fread.
bread contains 2 arguments, A function pointer (bNread) and the file pointer. But I am not sure I understand how it works.
The declaration for bread() in the Better String Library documentation is:
typedef size_t (*bNread)(void *buff, size_t elsize, size_t nelem, void *parm);
extern bstring bread(bNread readPtr, void *parm);
Therefore, bread() is a function that takes a pointer to a function as an argument. The bNread type is used to specify the type of function. The fread() function is close enough to pass muster when cast — the match isn't exact because it expects a FILE * for its fourth argument and a true function of the type pointed at by a bNread expects a void *. The second argument to bread is the value to be passed as the fourth argument to the function pointed to by the bNread.
So, in the code you're maintaining:
data = bread((bNread)fread, db_fp);
The Better String Library function is called with fread() as the I/O function, cast to the correct type to quell an otherwise justified compiler warning about a mismatch in types, plus the file stream that should be used for reading.
The implementation of the bread() function uses the function pointer and the stream pointer whenever it needs to do I/O in order to read a string. That is, as KerrekB also explained, the code
might write something like:
char buffer[256];
size_t nbytes = (*readPtr)(buffer, sizeof(char), sizeof(buffer), parm);
or (equivalently):
size_t nbytes = readPtr(buffer, sizeof(char), sizeof(buffer), parm);
(If you learned C long enough ago, the first was the only way to invoke functions through pointers to functions; since the C89 standard was produced, the second has been available and is probably used more widely these days.) There are a number of tricks that the implementation must worry about, but the basic function call would be somewhat similar. The code could use I/O functions other than fread(), though the interface to the function must be similar.
Pointers to functions are powerful, but are arcane until you've used them.
Here's one possible implementation of bread:
bstring bread(int(*func)(void *, size_t, size_t ,FILE *), FILE * fp)
{
char buf[10];
bstring result;
func(buf, 10, 1, fp);
return result;
}
This doesn't of course reflect what the real function bread is doing, but it should give you an idea how an implementation might use it.
(The real function would of course read a length from fp first, then allocate enough memory for the string, and then read the string, but the details depend on what bstring is and how it works. A real implementation would also check the return value of func!)
For example, if I have
FILE *fp, *tfp;
fp = fopen (file, mode);
tfp = fp;
is this possible?
Yes, it is possible and totally legal.
Both fp and tfp are pointers to a variable of type FILE
The statement tfp = fp; copies the address saved in fp into tfp. So, please keep in mind that you end up with two pointers to the same object.
You aren't duplicating the "object" created in the fopen() call at all.
Yes that it possible. These are just pointers after all. You can have as many variables as you like containing the same pointer and nobody can tell them apart.
This is correct. But the best way to learn is to try. Launch this code under debugger and check that this works as you expect.
Pointer variable (FILE * fp) simply contains an address (a number). You can see it:
printf( "%p\n", fp );
So, assigning numbers of same type is perfectly legal.