0

I'd like to ask why is it possible to change the pointer value of file in C function without passing reference to it, what I mean is:

void fun(FILE *f)
{
    fclose(f);
    f = fopen("newfile", "r");
}

int main(void)
{
    FILE *old = fopen("file", "r");
    char *msg = (char*)malloc(sizeof(char) * 100);
    fun(old);
    fscanf(old, "%s", msg);
    printf("%s", msg);
    free(msg);
    return 0;
}

Can anyone explain it to me? I was thought that pointers are being copied so I expected to get an error about closed file. Surprisingly I didn't get it.

bottaio
  • 4,963
  • 3
  • 19
  • 43
  • Standard Warning : Please [do not cast](http://stackoverflow.com/q/605845/2173917) the return value of `malloc()` and family. – Sourav Ghosh Feb 11 '15 at 12:14
  • @SouravGhosh Since when has not casting void * to the type you need become good practice? – BitTickler Feb 11 '15 at 12:15
  • @user2225104 Casting the return value of e.g. `malloc` can hide warnings given when `malloc` have not been properly prototyped (e.g. when the programmer forgot to include ``) – Some programmer dude Feb 11 '15 at 12:20
  • Not casting and later on adding those casts in a hurry when you compile with c++ compiler is more error prone. – BitTickler Feb 11 '15 at 12:20
  • `adding those casts in a hurry when you compile with c++ compiler` why you have to hurry? – Sourav Ghosh Feb 11 '15 at 12:22
  • You typically are in a hurry when you think you can simply reuse that piece of code and you did not plan time for those changes. You just want to get it to compile and you do not really care. With that, you lose all test depth you had otherwhise gotten for free. Just because someone tried to be clever. – BitTickler Feb 11 '15 at 12:24
  • @user2225104 Switching languages for a project should never be done in a hurry, even if they are somewhat similar like C and C++. And it will be very easy to find out the places where the casting is needed (or rather, where you should look into e.g. `std::vector` or in worst case change to using `new[]`) as you will get compiler *errors*. – Some programmer dude Feb 11 '15 at 12:25
  • @JoachimPileborg Obviously you never had to work under pressure. Why would you advise people to lay traps for future re-use in times where hardly anyone is compiling c code with a pure c-compiler anymore? – BitTickler Feb 11 '15 at 12:27
  • @user2225104 It's a trap only if you think that code from a completely different language can just be dropped into another language just like that. And I've had lot of pressure, hard to work for any time in the software industry without having pressure. – Some programmer dude Feb 11 '15 at 12:29
  • @JoachimPileborg People with your attitude make sure it is a completely different language, while it is perfectly reasonable to keep both worlds in mind while coding. – BitTickler Feb 11 '15 at 12:30
  • @user2225104 Then I equally childishly counter-argument with it's people like you that keep on treating C++ as an extended C ("C with classes"). C and C++ ***are*** different languages, with different ways of doing things. Would you do it the other way around, drop C++ code into a C project? Why not? How about other languages? Why not? – Some programmer dude Feb 11 '15 at 12:38
  • Let me put it this way: If you /intentionally/ prevent people from doing their work by making sure your code will break, you are not doing anyone a favor. Especially in the light of the fact that anytime I had to find bugs in C-code the first I did was to translate it with a c++ compiler to see type errors. Usually that got me quickly into to fixing major stuff the c-compiler silently accepted. – BitTickler Feb 11 '15 at 12:44
  • 1
    @user2225104 well, `gcc` provides a wide set of options that you should enable while compiling. Mostly it will do the job of warning you. And can you please give an example of a few wrong `stuff the c-compiler silently accepted.` I'm curious. :-) – Sourav Ghosh Feb 11 '15 at 12:50
  • @Sourav Ghosh Having C and C++ code in front of me for now over 20 years, having to use code from hardcore "I refuse c++ exists and stick to C code die hard" kind of guys on several occasions - had I bothered to keep all those situations when what I said above proofed true, I could now write a book and become as famous as Scott Meyers and maybe even rich. – BitTickler Feb 11 '15 at 12:53
  • @user2225104 if "it is perfectly reasonable to keep both worlds in mind while coding" but are happy to make the one you are *actually* coding less robust, why don't you use a `#define` for your casts, and then you will have it right in both languages? – Weather Vane Feb 11 '15 at 13:16
  • I am all for the #define NEW() solution. Better than simply smugly not casting and burdening the work on a future victim of that choice. – BitTickler Feb 11 '15 at 13:18
  • Why would I do that if I am writing in C? I have no idea you'll be hacking my code! – Weather Vane Feb 11 '15 at 13:20
  • @WeatherVane Ignorance is bliss o.O :) On a serious note: You gain much in the long run if you accept reality. And reality is that code lives longer than anyone expects it to live. And if it is useful it /will/ be reused in an unknown context. While it is impossible for non-psychics to foresee the future, it is simple to make choices which are painless and likely to be useful. C++ now also exists since like... 25 years. Nothing new at all. – BitTickler Feb 11 '15 at 13:25
  • @user2225104 Should my C code also consider `new()` in a way that is C# compatible? I fail to see why I should write my code in a way that makes your life easier, and mine harder. – Weather Vane Feb 11 '15 at 13:33
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/70706/discussion-between-user2225104-and-weather-vane). – BitTickler Feb 11 '15 at 13:34

2 Answers2

3

Arguments in C is passed by value, and that means they are copied. So the functions never have the original values, just copies, and modifying a copy will of course not modify the original.

You can emulate pass by reference by using pointers, and in the case of passing a pointer by reference you of course have to pass a pointer to the pointer (by using the address-of operator &).

void fun(FILE **f)
{
    fclose(*f);
    *f = fopen("newfile", "r");
}

...

fun(&old);
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I know what passing by value is. The point here is that even though I'm not passing an argument as pointer to pointer as you did here, I am getting pointer change in main. – bottaio Feb 11 '15 at 12:20
  • @greenshade I can promise you that the pointer `old` does *not* change value in `main` after the call. If it seems to work it's because of [*undefined behavior*](http://en.wikipedia.org/wiki/Undefined_behavior). – Some programmer dude Feb 11 '15 at 12:22
  • Thank you, that's the answer I wanted to get. Although it seems strange to me because I got code from my university and they told me to close a file inside a function and open it again but the pointer is being passed by copy. Maybe it's just a typo. – bottaio Feb 11 '15 at 12:26
  • 1
    As to *why* this happens, I suspect it's `fopen()` working via `open()`, which says `man open:...The file descriptor returned by a successful call will be the lowest-numbered file descriptor not currently open for the process.`. So after `fclose(); fopen()`, the new file has the same descriptor as the old one if the old one was the lowest-numbered file descriptor at the time. – EOF Feb 11 '15 at 16:26
0

It is copied, but it still points to the same file.

Michał Szydłowski
  • 3,261
  • 5
  • 33
  • 55
  • I don't get it. The pointer inside function points to the file which was open in main function but I closed this file and assigned new pointer to variable f and it shouldn't affect variable old in main function – bottaio Feb 11 '15 at 12:17