willywill997
willywill997

Reputation: 47

Passing pointer of a pointer in C

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:

  1. 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?

  2. 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

Answers (3)

John Bode
John Bode

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

prog-fh
prog-fh

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

Robert Harvey
Robert Harvey

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

Related Questions