Reputation: 47
I am currently studying how to work with pointers in C, and I have several questions if you dont mind.
I am trying to process information from files, therefore - to maintain modularity - I want to "send my files" into several helper methods.
My questions are:
I know that if I create these file pointers inside a method - they are kept inside the stack. Is it problematic (In a sense that it may end in unwanted behaviours) to send pointers from the stack memory to other methods?
I know that if i'll send the pointers of the files to other methods, it will actually create a copy of them, which means that changing them inside the method would not do anything at all.
Thats why im asking about "pointers to pointers", Ive read (but didn't quite get it) that I could send the helper methods pointers to the file pointers - and in that way, I would work on my actual files, rather than some local copy.
Lets say that mainFunction
is my main method (it is not in the "real" main method) which I use for processing files
void helperFunction1(FILE* file1, FILE* file2)
{
fclose(outputFile);
fclose(inputFile);
}
void helperFunction2(FILE* file1, FILE* file2)
{
**write some stuff into both files**
}
void mainFunction()
{
FILE* inputFile = fopen(filePath, "r");
FILE* outputFile = fopen(OUTPUT_FILE_NAME, "w");
helperFunction2(inputFile, outputFile);
helperFunction1(inputFile, outputFile);
}
Would the "real" inputFile
and outputFile
get closed from calling to helpFunction1
?
Would the "real" inputFile
and outputFile
get modified (get some stuff written into them) from calling to helpFunction2
?
I would love to get some insight / answers for my questions (I hope they are valid and not too pushy) and also I would love to get a brief explanation on how to work with "pointers to pointers" in order to modify data, rather than modifying some copies of it.
Upvotes: 1
Views: 557
Reputation: 123578
TL/DR - For what you're trying to do, helperFunction1
and helperFunction2
are declared correctly, you just need to use the correct names in helperFunction1
.
Longer version
If you want your function to modify the pointer object itself, then you must pass a pointer to that pointer. Here's a contrived example where we want to update a FILE *
object:
void open( FILE **ptr, const char *name, const char *mode )
{
*ptr = fopen( name, mode );
}
int main( void )
{
FILE *in; // in stores the address of a FILE object
FILE *out; // out stores the address of a FILE object
open( &in, "input.txt", "r" ); // we are changing the values of in and out
open( &out, "output.txt", "w" ); // so we must pass pointers to those objects
...
}
We want open
to modify the objects in
and out
, so we pass pointers to those objects.
Compare that to something like this:
void write( FILE *ptr, const char *text )
{
fwrite( ptr, "%s", text );
}
In this case, we're not trying to change the file pointer itself, we're just trying to write to the FILE
stream it points to, so we don't need to worry about a pointer to a pointer in this case. Another contrived example:
int main( void )
{
FILE *out;
...
write( out, "some text" ); // we are not changing the value of out,
... // so we do not pass a pointer to it
}
For any type T
, if we want a function to modify an object of that type, we must pass a pointer to that object:
void foo( T *ptr )
{
*ptr = new_T_value(); // write a new value to the thing ptr points to
}
int main( void )
{
T var;
foo( &var ); // write a new value to var
}
This works exactly the same way for pointer types - replace T
with P *
:
void foo( P * *ptr ) // or just P **ptr
{
*ptr = new_Pstar_value(); // write a new *pointer* value to the thing ptr points to
}
int main( void )
{
P * var;
foo( &var ); // write a new value to var
}
Again, this is only true if you want to modify the value of var
.
You can go with even higher levels of indirection - replace P
with Q *
:
void foo( Q * * *ptr ) // or just Q ***ptr
{
*ptr = new_Qstarstar_value(); // write a new *pointer* value to the thing ptr points to
}
int main( void )
{
Q * * var; // or just Q **var
foo( &var ); // write a new value to var
}
The expression *ptr
in foo
has the same type as the expression var
in main
, so writing to *ptr
is equivalent to writing to var
.
Upvotes: 1
Reputation: 16920
Here is a short example to illustrate the difference between passing the
FILE *
by value (to use it) and passing it by address (to re-assign it).
In use_file()
, the FILE *
itself is not changed but the
state of the (opaque) FILE
it points to is altered by the operation
performed.
In change_file()
we can change the fp
variable in main()
because we know its address.
Thus, fp
does not point to the same (opaque) FILE
before
and after the call to change_file()
.
/**
gcc -std=c99 -o prog_c prog_c.c \
-pedantic -Wall -Wextra -Wconversion \
-Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
-g -O0 -UNDEBUG -fsanitize=address,undefined
$ ./prog_c
$ cat output_1.txt
first line from main()
something new from use_file()
about to close in change_file()
$ cat output_2.txt
just open from change_file()
second line from main()
**/
#include <stdio.h>
void
use_file(FILE *f)
{
fprintf(f, "something new from %s()\n", __func__);
}
void
change_file(FILE **inout_f)
{
FILE *f=*inout_f; // load inout-parameter
fprintf(f, "about to close in %s()\n", __func__);
fclose(f);
f=fopen("output_2.txt", "w");
fprintf(f, "just open from %s()\n", __func__);
*inout_f=f; // store out-parameter
}
int
main(void)
{
FILE *fp=fopen("output_1.txt", "w");
fprintf(fp, "first line from %s()\n", __func__);
use_file(fp);
change_file(&fp);
fprintf(fp, "second line from %s()\n", __func__);
fclose(fp);
return 0;
}
(nb: no check for anything bad here, this is just illustrative)
Upvotes: 0
Reputation: 180908
When you call a function and pass an argument to it, C makes a copy of that argument. If it is a value type (i.e. it doesn't have an asterisk after the type specifier), the copy of the argument is the value of that value type. But if the argument is a pointer type, what gets copied is the memory address that the pointer is pointing to.
Which means that you now have two pointers pointing to the same data in memory. It also means that you can modify that data from either pointer reference.
So yes, you're operating on the original FILE.
Upvotes: 1