First of all, we need to know the compilation cycle of a C source file. Basically, it goes through the following stages:
- preprocessor
- compilation for object
- linkediting
The linkediting process takes place when constructing the final executable. Intermediate build files stop at step 2 compilation for object. I gave more details in this answer.
Taking some context of the linked answer:
Functions they had forward declaration [are open references].
You can and should create object files with open references. But you can only create executable after closing all static references, after making all the links, all the Binds. And who does that? The linker.
What is this about forward declaration? It is a technique in which I teach the compiler that something will exist further; I declare that something will exist. But, I do not define much about it =) Read more (the example is in C++ but the idea is the same, declare something forward).
But why is it important to meet this forward declaration? Because...
Amazingly, the C compiler works only a single input at a time. It takes that input from the so-called C pre-processor.
Source
The case study: copiartxt
You want to create a function that takes two file pointers, that has no return, and that is called copiartxt
. In order to use it in several corners, you have to compile it partially and you can use this result in the linkediting and generate the final executable.
For such a process, there is the file biblioteca.c
, which will be transformed into biblioteca.o
(or biblioteca.obj
, in the Visual Studio world if I’m not mistaken). Let’s build it gradually?
To begin with, shall we focus on her? She will have the function copiartxt
desired. So let’s start with this:
void copiartxt(FILE *file1, FILE *file2)
{
char leitor[1000];
while (fgets(leitor, 1000, file1) != NULL) {
fputs(leitor, file2);
}
}
You may notice that our codes are slightly different. I used my code style, keys positioning and spacing, but also removed the return 0;
, since the function should have no return, it is void
If you so to compile, some compilers will complain that they do not know fgets
, nor fputs
, nor NULL
, I believe that also of FILE
. These functions and this constant are declared in the file <stdio.h>
. About fgets
, at some point <stdio,h>
you will find a line similar to this (source):
char *fgets(char *str, int n, FILE *stream);
Just like that. Just the statement line of that function. So, we need to insert this header in our biblioteca.c
:
#include <stdio.h>
void copiartxt(FILE *file1, FILE *file2)
{
char leitor[1000];
while (fgets(leitor, 1000, file1) != NULL) {
fputs(leitor, file2);
}
}
All right, now you should compile happy and error free. After compiling up to object code level (gcc -c biblioteca.c
, for example), we can use it in our executable.
Now, let’s go to the executable? main.c
the name? Well, here we go. The central reason of this file is an application entry point (the function main
) that will copy the file "Arquivo 1.txt"
to fate "Arquivo 2.txt"
. Should give some treatment for the bugs too, so come on, encode the function main
:
int main(void)
{
FILE *file1 = fopen("Arquivo 1.txt", "r");
if (file1 == NULL) {
fprintf(stderr, "ERRO ao abrir \"Arquivo 1.txt\"!");
return 1;
}
FILE *file2 = fopen("Arquivo 2.txt", "w");
if (file2 == NULL) {
fprintf(stderr, "ERRO ao abrir \"Arquivo 2.txt\"!");
return 1;
}
copiartxt(file1, file2);
fclose(file1);
fclose(file2);
return 0;
}
Changes that I made:
- removed the forward declaration that was within the function
main
, then we’ll deal with it
- added an error detection to the
"Arquivo 2.txt"
in the same style you made for "Arquivo 1.txt"
(will we are in a read-only file system?)
- I put a slightly more specific error message; now you can discern who was the cause of the error
- I am printing on error output
stderr
, for that is where the errors must be printed; for this, it was necessary to use the fprintf
If you try to compile without further changes, the main.c
will present some errors. As for example:
- what is
NULL
?
- what is
stderr
?
- where the function
fprintf
?
- where the function
fopen
?
- where the function
fclose
?
- where the function
copiartxt
?
Maybe I’ll complain about what it is FILE
, do not remember. How to fix this? Well, a good part is solved by including the <stdio.h>
. Then I’d be like this:
#include <stdio.h>
int main(void)
{
FILE *file1 = fopen("Arquivo 1.txt", "r");
if (file1 == NULL) {
fprintf(stderr, "ERRO ao abrir \"Arquivo 1.txt\"!");
return 1;
}
FILE *file2 = fopen("Arquivo 2.txt", "w");
if (file2 == NULL) {
fprintf(stderr, "ERRO ao abrir \"Arquivo 2.txt\"!");
return 1;
}
copiartxt(file1, file2);
fclose(file1);
fclose(file2);
return 0;
}
But still no statement of who you are copiartxt
. We can use a forward declaration and the compiler will be happy.
ATTENTION: is not the final code
#include <stdio.h>
void copiartxt(FILE *fonte, FILE *destino);
int main(void)
{
FILE *file1 = fopen("Arquivo 1.txt", "r");
if (file1 == NULL) {
fprintf(stderr, "ERRO ao abrir \"Arquivo 1.txt\"!");
return 1;
}
FILE *file2 = fopen("Arquivo 2.txt", "w");
if (file2 == NULL) {
fprintf(stderr, "ERRO ao abrir \"Arquivo 2.txt\"!");
return 1;
}
copiartxt(file1, file2);
fclose(file1);
fclose(file2);
return 0;
}
Okay, now the compiler is satisfied. But, will that be right?
You have to declare main.c
a thing that is external to it. If there were anything that gave this effect, teach the compiler that there are forward declaration of functions without having to be in the archive main.c
...
But, wait a minute! That’s it! That’s what the pre-compilation directive #include
does, it "writes" to the source file. So, if there magically exists a biblioteca.h
that declares this function, I am done. The code of the main.c
would then be:
#include <stdio.h>
#include "biblioteca.h"
int main(void)
{
FILE *file1 = fopen("Arquivo 1.txt", "r");
if (file1 == NULL) {
fprintf(stderr, "ERRO ao abrir \"Arquivo 1.txt\"!");
return 1;
}
FILE *file2 = fopen("Arquivo 2.txt", "w");
if (file2 == NULL) {
fprintf(stderr, "ERRO ao abrir \"Arquivo 2.txt\"!");
return 1;
}
copiartxt(file1, file2);
fclose(file1);
fclose(file2);
return 0;
}
Okay, now everyone’s happy. We just need to create the biblioteca.h
, right? Actually, more or less... if you want the file header is coherent, who implements the functions he declared should also include the same header. If I don’t make that inclusion, I could set a function copiartxt
with another signature, other entries. So this means that it is also necessary to make some changes to the biblioteca.c
. In this case, the amendment is simply to include the biblioteca.h
and be happy:
#include <stdio.h>
#include "biblioteca.h"
void copiartxt(FILE *file1, FILE *file2)
{
char leitor[1000];
while (fgets(leitor, 1000, file1) != NULL) {
fputs(leitor, file2);
}
}
Okay, now just create the file biblioteca.h
. As always, let’s start at its heart: the forward declaration of function copiartxt
:
void copiartxt(FILE *file1, FILE *file2);
Well, now the compiler can complain that he doesn’t know the type FILE
. So there’s nothing fairer than teaching at header who he is:
#include <stdio.h>
void copiartxt(FILE *file1, FILE *file2);
Now ready? Well, not yet... files header in C need special care. You normally do not want it to be included more than once in the code. And yes, this can happen. For example, in biblioteca.c
, there are two guidelines for inclusion of <stdio.h>
; the first, in the archive itself biblioteca.c
, and the second time in #include
of biblioteca.h
. This is part of the pre-processing of the C language. One of the alternatives to doing this is through guardians of inclusion (include Guardians). A classic inclusion guardian uses two pre-processor features:
- I can send a text to the compiler in a conditional manner; therefore, I can ignore text in a conditional manner
- can declare things
Setting an example of this question:
#ifndef FOO_H_INCLUDED
#define FOO_H_INCLUDED
class Foo
{
// código
};
#endif
He’s protecting the code by declaring FOO_H_INCLUDED
. So we can use that in our biblioteca.h
:
#ifndef BIBLIOTECA_H_INCLUDED
#define BIBLIOTECA_H_INCLUDED
#include <stdio.h>
void copiartxt(FILE *file1, FILE *file2);
#endif
Note that the inclusion guardian should be unique to each header, otherwise you can activate another’s guardian header and cause a confusion
There’s also a certain #pragma once
which does something similar to the inclusion guardian, but is more efficient. The problem is that it is not standard, so not all compilers will accept it. Read more.
But why did it work with void copiartxt(struct FILE *origem, struct FILE *destino)
?
Basically because, with pointers, several C compilers will ignore whether there is such of the stated structure. Treating only as pointers, many C compilers can make absurd and disgusting mixes, but ultimately they work. That’s why you can use a pointer to a structure that has not been declared (but is called struct FILE
).
In the case of <stdio.h>
, at no time does she make available to you a struct FILE
, because you don’t need to know what a FILE
internally. For example, in <stdio.h>
made available by GNU, line 51:
typedef struct __sFILE FILE;
Additional notes
If you want C++ compatibility, just include all forward declaration within a block extern "C"
. But just do it for C++, C doesn’t know blocks extern "C"
:
#ifndef BIBLIOTECA_H_INCLUDED
#define BIBLIOTECA_H_INCLUDED
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
void copiartxt(FILE *file1, FILE *file2);
#ifdef __cplusplus
}
#endif
#endif
In short
Filing cabinet biblioteca.h
(compatible with C++):
#ifndef BIBLIOTECA_H_INCLUDED
#define BIBLIOTECA_H_INCLUDED
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
void copiartxt(FILE *file1, FILE *file2);
#ifdef __cplusplus
}
#endif
#endif
Filing cabinet biblioteca.c
:
#include <stdio.h>
#include "biblioteca.h"
void copiartxt(FILE *file1, FILE *file2)
{
char leitor[1000];
while (fgets(leitor, 1000, file1) != NULL) {
fputs(leitor, file2);
}
}
Filing cabinet main.c
:
#include <stdio.h>
#include "biblioteca.h"
int main(void)
{
FILE *file1 = fopen("Arquivo 1.txt", "r");
if (file1 == NULL) {
fprintf(stderr, "ERRO ao abrir \"Arquivo 1.txt\"!");
return 1;
}
FILE *file2 = fopen("Arquivo 2.txt", "w");
if (file2 == NULL) {
fprintf(stderr, "ERRO ao abrir \"Arquivo 2.txt\"!");
return 1;
}
copiartxt(file1, file2);
fclose(file1);
fclose(file2);
return 0;
}
How are you compiling? What error message is displayed? I can already point to half a dozen "mistakes" or "pattern leaks," but I don’t know if it’s really the point that’s actually stopping you
– Jefferson Quesado
Is giving conflict in copiartxt in main. c
– Gustavo Pavanati
which error message?
– Jefferson Quesado
In Function 'main': [Error] Conflicting types for 'copiartxt'
– Gustavo Pavanati
Which file? And how are you doing to compile?
– Jefferson Quesado
In main.c. I pulled that line from the main program and there were other errors in the library.h. And it’s just in this library. h that I have doubt, because I have never made a program to touch files of this type
– Gustavo Pavanati
Oops, I was able to fix it. I should have put "struct FILE *" in the library.h. It worked as expected.
– Gustavo Pavanati
By your description, keep using the wrong way. When you have a time I answer the correct
– Jefferson Quesado
Okay buddy. I’m waiting for your version!!
– Gustavo Pavanati
Related: https://answall.com/q/307279/64969
– Jefferson Quesado