0

I need to initialise FILE *fi in different function. This example code shows the problem.

#include<iostream>
using namespace std;

void init(FILE* fi) 
{ 
    fi=fopen("in.txt", "r");    
    if(fi==NULL) 
    {       
        cout<<"Got NULL\n";     
    } 
}

int main()  
{   
    FILE* fi;   
    init(fi);   
    if(fi==NULL)    
    {       
        cout<<"NULL\n";         
        return 0;   
    }   
    return 0;  
}

The program outputs NULL (not "Got NULL"), and I don't have a clue how to make it work...


It is important that I have fi passed as a pointer, not getting it as a return value.

KBOPYM
  • 33
  • 6
  • In.txt is in same directiory as your executable file? – iRhonin May 05 '18 at 18:34
  • yes, no problems with files, pointer fi is equal to NULL in main, but not in init – KBOPYM May 05 '18 at 18:35
  • 1
    `fi` is effectively a local variable of the `init` function. You need to pass the pointer by reference. –  May 05 '18 at 18:35
  • 1
    Given this is tagged C++, please read up in "pass by reference" and how parameters are passed in C++. Because --obviously-- the value of (the pointer) `fi` is passed by value into `init()` here and thus any changes to that value inside of `ìnit()` become void outside of `init()`. – dhke May 05 '18 at 18:36
  • Likely duplicate: https://stackoverflow.com/questions/11842416/ – Drew Dormann May 05 '18 at 18:43

2 Answers2

1

You shouldn't manipulate arguments unless that's the specific intent of the code. It's better to simply return things:

FILE* init()
{ 
    return fopen("in.txt", "r");
}

Then you call it like this:

FILE* fi = init();

Note that since you're using C++ you should avoid using C anachronisms like FILE and instead use C++ file streams.

If you need to handle multiple return values:

std::tuple<FILE*, FILE*> init() {
  return std::make_tuple(
    fopen("in.txt", "r"),
    fopen("out.txt", "w")
  );
}

This is just a step towards making it proper C++ code by encapsulating all of this inside a proper class definition and then you can manipulate properties directly:

class FileWrapper {
public:
  FileWrapper();
  ~FileWrapper();
  void init();

protected:
  FILE *fi;
  FILE *fo;
}

void FileWrapper::init() {
  fi = fopen("in.txt", "r");
  fo = fopen("out.txt", "w");
}

In that case you don't even need return values. You'll also be able to automatically clean up those filehandles when that wrapper class gets deallocated if you write a proper destructor.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • It is important that I have fi passed as a pointer, not getting it as a return value. – KBOPYM May 05 '18 at 18:46
  • Why is that important? You'll need to clarify because in your code it's not the case. In old-school C you'd use a pointer-pointer occasionally for such things, but in C++ there's a strong philosophy built up around returning objects that are cheap to copy. – tadman May 05 '18 at 18:46
  • My code is just an example, I'm trying to have pointers pointing to stdin and stdout of a child process, thats two of them, so its easier to pass them as pointers – KBOPYM May 05 '18 at 18:55
  • You might think it's easier, but there's usually a better way than manipulating arguments. Don't pass in mutable arguments, instead save return values. For example, you can return a simple `tuple` or `struct` which contains the values. – tadman May 05 '18 at 18:57
0

OK, got it.

Replase

void init(FILE* fi) 

with

void init(FILE*&fi) 
KBOPYM
  • 33
  • 6