1

I'm trying to relearn C from dabbling with it about 5 year ago. Specifically, I'm trying to learn how to extract a number of operations from main and make them into a function, with the aim of moving them to a library file next.

This seems to be working:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

struct arguments {
  char *word_file;  /* Default name for input file */
} arguments;

int main (int argc, char *argv[]) {

  arguments.word_file = "dummy";
  
  FILE *fp;
  fp = fopen(arguments.word_file, "r");
  if (fp == NULL) { /* If fopen failed... */
    fprintf(stderr, "Error: Unable to open file %s: %s\n",
        arguments.word_file, strerror (errno));
    exit (8);
  }

  char word[60];
  fgets (word, sizeof(word), fp);
  printf("Word is %s\n", word);
}

By the way, 'dummy' is:

$ cat dummy
dog
cat
$ 

No matter how I try this, it either gives me compile errors, or seg faults when I run it:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

struct arguments {
  char *word_file;  /* Default name for input file */
} arguments;

void getfile(FILE *fp) {  
  fp = fopen(arguments.word_file, "r");
  if (fp == NULL) { /* If fopen failed... */
    fprintf(stderr, "Error: Unable to open file %s: %s\n",
        arguments.word_file, strerror (errno));
    exit (8);
  }
}

int main (int argc, char *argv[]) {

  arguments.word_file = "dummy";
  
  FILE *fp;
  getfile(fp);
  
  char word[60];
  fgets (word, sizeof(word), fp);
  printf("Word is %s\n", word);
}

I've tried changing from *fp to fp to &fp without success. I'm sure that there's something that I don't understand about file pointers, but can't figure it out.

Thanks for any help and suggestions.

-Kevin

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • C passes arguments by value, so the `fp` in `getfile` is a different variable than the `fp` in `main`. – user3386109 Apr 05 '22 at 20:40
  • The compiler should give you a warning on the line that calls `getfile` that you are using an uninitialized variable. It's important that you enable the compiler warnings, and fix all the warnings. If your compiler is gcc or clang, compile with `-Wall -Wextra -Werror`. – user3386109 Apr 05 '22 at 20:44

3 Answers3

1

fp shouldn't be an argument to getfile(), it should be the return value.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

struct arguments {
  char *word_file;  /* Default name for input file */
} arguments;

FILE *getfile() {  
  FILE *fp = fopen(arguments.word_file, "r");
  if (fp == NULL) { /* If fopen failed... */
    fprintf(stderr, "Error: Unable to open file %s: %s\n",
        arguments.word_file, strerror (errno));
    exit (8);
  }
  return fp;
}

int main (int argc, char *argv[]) {

  arguments.word_file = "dummy";
  
  FILE *file_ptr;
  file_ptr = getfile();
  
  char word[60];
  fgets (word, sizeof(word), file_ptr);
  printf("Word is %s\n", word);
}

If there's some reason you really need to pass it as a parameter, see Changing address contained by pointer using function

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Thanks for your really quick reply. This seems to me to be an unsafe practice. Once the function getfile() is tucked way in a library file, it seems easy to forget that the magic name is 'fp' and someone might rename it 'file_ptr' in the main routine. – Kevin Zembower Apr 05 '22 at 20:46
  • It's not a magic name, you can use any variable you want. – Barmar Apr 05 '22 at 20:51
  • But don't I have to use 'fp' in both the main function, and the getfile() function? – Kevin Zembower Apr 05 '22 at 20:53
  • No, why would you? It's just returning a value, and then assigning that to a variable in the caller. Why do you think this is any different from the way `fp = fopen(...)` works? – Barmar Apr 05 '22 at 20:56
  • Wow, you're right, it compiles and works. I'll have to study this to understand it. Thanks, again, for your help. – Kevin Zembower Apr 05 '22 at 20:57
  • Now I see that you're creating two different filepointers, one named 'fp' in the getfile() fucntion, and another one in main called file_ptr. I think I'm beginning to understand. Thanks. – Kevin Zembower Apr 05 '22 at 20:59
1

You have two choices

First, have 'getfile' return the file handle (this is the most idiomatic way in c)

FILE *getfile() {  
  FILE *fp = fopen(arguments.word_file, "r");
  if (fp == NULL) { /* If fopen failed... */
    fprintf(stderr, "Error: Unable to open file %s: %s\n",
        arguments.word_file, strerror (errno));
    exit (8);
  }
  return fp;
}

and in main

FILE *fp =  getfile(fp);

or have getfile update the fp value based , using c-style 'pass by reference'

void getfile(FILE **fp) {  
  *fp = fopen(arguments.word_file, "r");
  if (*fp == NULL) { /* If fopen failed... */
    fprintf(stderr, "Error: Unable to open file %s: %s\n",
        arguments.word_file, strerror (errno));
    exit (8);
  }
}

in main

File *fp = NULL;
getfile(&fp);
pm100
  • 48,078
  • 23
  • 82
  • 145
  • thanks so much for your quick response. I think the second way you described is what I was thinking of. I would have never got the '**' part. I'll have to study it some more. Thanks, again. – Kevin Zembower Apr 05 '22 at 20:52
  • @KevinZembower you pass a vlue to a functin as an argument, you want it to be updated -> you must pass a pointer to it. In this case the thing you want to pass and have updated is itself a pointer -> pass `&fp`, with arg type `FILE**` – pm100 Apr 05 '22 at 21:01
0

The problem is that the pointer fp is passed to the function getfile by value.

getfile(fp);

That is the function deals with a copy of the value of the passed pointer fp. So changing the copy in the function does not reflect on the value of the original pointer.

You need to pass the pointer by reference.

In C passing by reference means passing an object indirectly through a pointer to it. Dereferencing the pointer you can get a direct access to the original object.

So declare and define the function like

void getfile(FILE **fp) {  
  *fp = fopen(arguments.word_file, "r");
  if ( *fp == NULL) { /* If fopen failed... */
    fprintf(stderr, "Error: Unable to open file %s: %s\n",
        arguments.word_file, strerror (errno));
    exit (8);
  }
}

and call the function like

getfile(&fp);
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335