Appeler une fonction à partir D'un autre fichier dans le même répertoire en C

j'apprends le C, mais j'ai une longue expérience avec les langages de programmation de niveau supérieur comme Java.

je lisais sur les fichiers d'en-tête donc je jouais avec eux, cependant j'ai remarqué que je pouvais appeler une fonction à partir d'un autre fichier sans #l'inclure (c'est dans le même répertoire), comment est-ce possible ?! Est-ce le fichier make, linker qui est configuré de cette façon ou quoi ?

Nous avons deux fichiers

main.c
add.c

main.c appelle la fonction add(int x,int y) de ajouter ajouter.c, mais j'ai compilé par erreur avant #incluant add.c et cela a fonctionné ! Ce qui rend les choses plus confuses, c'est que lorsque j'y ajoute un ajout.c, ça donne un multiple de définition de l'erreur sur la fonction ajouter

21
demandé sur MohamedEzz 2011-07-08 05:10:43

4 réponses

il y a plusieurs choses qui se passent ici. Tout d'abord, je vais examiner comment la compilation de base de plusieurs fichiers fonctionne.

si vous avez plusieurs fichiers, l'important est la différence entre la déclaration et la définition d'une fonction. La définition est probablement ce à quoi vous êtes habitués lorsque vous définissez des fonctions: vous écrivez le contenu de la fonction, comme

int square(int i) {
    return i*i;
}

La déclaration, d'autre part, vous permet de déclarer au compilateur que vous en savez un la fonction existe, mais on ne dit pas au compilateur ce qu'elle est. Par exemple, vous pourriez écrire

int square(int i);

et le compilateur s'attendrait à ce que la fonction "carré" soit définie ailleurs.

Maintenant, si vous avez deux fichiers que vous souhaitez interagir (par exemple, supposons que la fonction "carré" est défini à l'ajouter.c, et que vous voulez l'appeler carrés(10) dans la principale.c), vous devez faire une définition et une déclaration. Tout d'abord, vous définissez carré en ajouter.c. Ensuite, vous déclarer au début de la main.C. Ceci permet au compilateur de savoir quand il compilera main.c qu'il y a une fonction "carré" qui est définie ailleurs. Maintenant, vous devez compiler les deux main.c et ajouter.c dans les fichiers d'objets. Vous pouvez le faire en appelant

gcc -c main.c
gcc -c add.c

cela produira les fichiers main.o et ajouter.O. Ils contiennent les fonctions compilées, mais ne sont pas tout à fait exécutables. La chose importante à comprendre ici est que les principaux.o est "incomplète" dans un sens. Lors de la compilation main.o, vous lui avez dit que la fonction "carré" existe, mais la fonction "carré" n'est pas défini, à l'intérieur.o. Donc principal.o a une sorte de" référence pendante "à la fonction"carré". Il ne sera pas compilé dans un programme complet à moins que vous le combiniez avec un autre .o (ou A. ou alors .un fichier qui contient une définition de "carré". Si vous essayez de faire lien main.o dans un programme, i.e.

gcc -o executable main.o

Vous obtiendrez une erreur, parce que le compilateur va essayer de résoudre la référence pendante à la fonction "carré", mais ne trouvera pas de définition pour elle. Cependant, si vous incluez ajouter.o lors de la liaison (liaison est le procédé de résolution toutes ces références à des fonctions non définies lors de la conversion .o fichiers vers exécutables ou .donc des fichiers), alors il n'y aura pas de problème. i.e.

gcc -o executable main.o add.o

alors c'est comme ça qu'il faut fonctionnellement utiliser des fonctions à travers les fichiers C, mais style, ce que je viens de vous "n'est pas de la bonne façon". La seule raison pour laquelle je l'ai fait est que je pense que cela vous aidera à mieux comprendre ce qui se passe, plutôt que de compter sur "#include magic". Maintenant, vous avez peut-être remarqué avant que les choses deviennent un peu embrouillées si vous devez re-écl arer chaque fonction que vous voulez utiliser au sommet de main.c C'est pourquoi les programmes C utilisent souvent des fichiers d'aide appelés "headers" qui ont un .h extension. L'idée d'un en-tête, c'est qu'il contient les déclarations de fonctions, sans leurs définitions. De cette façon, afin de compiler un programme utilisant des fonctions définies dans ajouter.c, vous n'avez pas besoin de déclarer manuellement toutes les fonctions que vous utilisez, pas plus que vous n'avez besoin d'inclure #tout le add.fichier c dans votre code. Au lieu de cela, vous pouvez #include ajouter.h, qui contient simplement le déclarations de toutes les fonctions de l'ajouter.c.

maintenant, une mise à jour sur #include: # include copie simplement le le contenu d'un fichier directement dans l'autre. Ainsi, par exemple, le code

abc
#include "wtf.txt"
def

est exactement l'équivalent de

abc
hello world
def

en supposant que wtf.txt contient le texte "hello world".

Donc, si nous mettons toutes les déclarations d'ajouter.c en ajouter.h (c'est à dire

int square(int i);

et puis, au sommet de la principale.c, nous écrire

#include "add.h"

C'est fonctionnellement le même que si nous avions fait manuellement déclaré la fonction "carré" en haut de principal.c.

Donc l'idée générale de l'aide d'en-têtes est que vous pouvez avoir un fichier spécial qui déclare automatiquement toutes les fonctions dont vous avez besoin en seulement #inclure.

cependant, les en-têtes ont aussi une utilisation plus courante. Supposons que principal.c utilise des fonctions à partir de 50 fichiers différents. Le dessus de la main.c ressemblerait à ceci:

#include "add.h"
#include "divide.h"
#include "multiply.h"
#include "eat-pie.h"
...

au lieu de cela, les gens déplacent souvent tous ces #includes vers la main.h fichier d'en-tête, et juste #include principal.h de main.c. Dans ce cas, le fichier d'en-tête sert deux fins. Il déclare les fonctions principales.c pour utilisation lors de l'inclusion par d'autres fichiers, et il comprend toutes les dépendances de main.c lorsqu'ils sont inclus dans main.C. L'utiliser de cette façon permet aussi chaînes de dépendances. Si vous #incluez add.h, vous donne non seulement les fonctions définies dans ajouter.c, mais vous obtenez aussi implicitement toutes les fonctions qui ajoutent.c utilise, et toutes les fonctions ils utilisation, et donc sur.

aussi, plus subtilement, #incluant un fichier d'en-tête de son propre .le fichier c vérifie implicitement les erreurs que vous faites. Par exemple, si vous avez malencontreusement défini carré

double square(int i);

en ajouter.h, vous ne vous en rendriez pas compte tant que vous n'auriez pas relié cette ligne principale.o est à la recherche d' définition de la place, et ajouter.o est de fournir un autre, incompatible. Cela va vous causer des erreurs lors de la liaison, de sorte que vous n'aurez pas compte de l'erreur jusqu'à plus tard dans le processus de construction. Cependant, si vous #incluez add.h à partir de ajouter.c pour le compilateur, votre fichier est de la forme

#include "add.h"
int square(int i) {
    return i*i;
}

qui après le traitement de l'instruction #include ressemblera

double square(int i);
int square(int i) {
    return i*i;
}

que le compilateur remarquera lors de la compilation add.c, et de vous en parler. Effectivement, inclure votre propre en-tête de cette façon vous empêche de faire de la publicité mensongère à d'autres dossiers le type des fonctions que vous fournissez.

Pourquoi vous pouvez utiliser une fonction, sans jamais déclarer

comme vous l'avez remarqué, dans certains cas, vous pouvez réellement utiliser une fonction sans la déclarer ou #incluant tout fichier qui la déclare. C'est stupide, et tout le monde est d'accord que c'est stupide. Cependant, c'est une fonctionnalité héritée du langage de programmation C (et des compilateurs C) que si vous utilisez une fonction sans la déclarer en premier, elle suppose simplement qu'il s'agit d'une fonction retournant le type "int". Ainsi, en effet, l'utilisation d'une fonction est déclarer implicitement cette fonction comme une fonction qui retourne "int" si elle n'est pas déjà déclarée. C'est un comportement très étrange si vous y pensez, et le compilateur devrait vous prévenir si vous le faites.

En-Tête De Gardes

Une autre pratique courante est l'utilisation de "Tête de Gardes". Pour expliquer les en-têtes, examinons un problème possible. Disons que nous avons deux dossiers: herp.c, et derp.c, et ils vous voulez utilisez les fonctions contenues l'une dans l'autre. En suivant les directives ci-dessus, vous pourriez avoir un herpès.h avec la ligne

#include "derp.h"

et un derp.h avec la ligne

#include "herp.h"

maintenant, si vous y pensez, # include " derp.h" sera converti en contenu de derp.h, qui à son tour contient la ligne # include " herp.h", qui sera converti au contenu de herp.h et contient... et ainsi de suite, de sorte que le compilateur va continuer pour toujours comprendre. De même, si le principal.h # inclut les deux herpès.h et derp.h, et à la fois de reptiles et d'amphibiens.h et derp.h y compris add.h, on voit ça dans main.h, on finit avec deux copies d'add.h, l'un à la suite de #y compris de reptiles et d'amphibiens.h, et un à la suite de l'inclusion du derp.h. Donc, la solution? Un "Header guard", c'est-à-dire un morceau de code qui empêche tout en-tête d'être #inclus deux fois. Pour ajouter.h, par exemple, la façon normale de faire c'est:

#ifndef ADD_H
#define ADD_H

int sqrt(int i);
...
#endif

ce morceau de code est essentiellement dire au préprocesseur (la partie du compilateur qui gère toutes les instructions "#XXX") de vérifier si "ADD_H" est déjà défini. Si elle n'est pas (si n def) puis il définit D'abord" ADD_H " (dans ce contexte, ADD_H n'a pas à être défini rien, c'est juste un booléen qui est soit définie ou non), et définit ensuite le reste du contenu de l'en-tête. Cependant, si ADD_H est déjà défini, alors #incluant ce fichier fera rien, parce qu'il n'y a rien en dehors du bloc #ifndef. Donc, l'idée est que seul le premier temps, il est inclus dans un fichier donné, il sera de fait d'ajouter n'importe quel texte dans ce fichier. Après cela, #including n'ajoutera pas de texte supplémentaire à votre fichier. ADD_H est juste un symbole arbitraire que vous choisissez pour garder une trace de si add.h a été inclus encore. Pour chaque en-tête, vous utilisez un symbole différent pour savoir s'il a été inclus ou non. Par exemple, de reptiles et d'amphibiens.h utiliserait probablement HERP_H au lieu de ADD_H. L'utilisation d'un" header guard " va corriger n'importe lequel des problèmes que j'ai énumérés ci-dessus, où vous avez des copies dupliquées d'un dossier inclus, ou une boucle infinie de #includes.

79
répondu Jeremy Salwen 2016-10-06 13:59:27

Le problème est que vous ne devriez pas être #includeing .c fichier.

Pour utiliser une fonction dans un autre fichier, vous devez le déclarer. Généralement, chaque .fichier c (sauf main.c) a un en-tête associé (.h) fichier correctement déclare toutes les fonctions définies dans le .c fichier. Vous pouvez déclarer autant de fois que vous voulez (tant que toutes les déclarations sont identiques), mais il ne peut y en avoir qu'une définition.

Ce qui se passe quand vous #include "add.c" c'est que le texte de l'ajouter.c est inclus dans main.c, donnant la main.c définition (et, comme un effet secondaire, une déclaration)add. Ensuite, lorsque vous compilez ajouter.c sur c'est propre, qui crée un autre définition de l' add. Ainsi, il y a deux définitions de la fonction, et le compilateur panique parce qu'il ne sait pas laquelle utiliser.

Si vous le remplacez #include "add.h", où ajouter.h ressemble à quelque chose comme ceci:

#ifndef ADD_H
#define ADD_H

extern int add(int x, int y);

#endif /* ADD_H - Google "include guard" for more info about this trickery */

puis principal.c a une déclaration de add et peut utiliser la fonction, mais le définitionadd est assez fermement que dans le complément.le fichier c, et donc il n'existe qu'une seule fois, et donc il compilera correctement.

4
répondu Chris Lutz 2011-07-08 01:26:37

voici un exemple simple d'appel d'une fonction à partir d'un programme c différent

permettez-moi de nommer le programme principal comme principal.c et le programme qui tient la fonction comme fonction.c pour la fonction.c je crée le fichier d'en-tête appelé fonction.h

main.c

#include"function.h"
int main()
{
     int a = sum(1,2);
     return a;
}

fonction.c

int function(int a,int b)
{
    return a+b;
}

fonction.h

int function(int,int);

Pour compiler utiliser la commande ci-dessous

g++ main.c de la fonction.c-o principal

Voici l'explication détaillée. Dans le programme principal, j'ai appelé la fonction de somme de 2 nombres. Les valeurs 1 et 2 du programme principal ont été transmises à la fonction de la fonction.c par l'intermédiaire de la fonction d'en-tête.h qui retient le point d'accès ou le pont de la fonction.C

Pour plus de détails, vous pouvez consulter les liens ci-dessous

http://www.cplusplus.com/forum/beginner/34691/

https://social.msdn.microsoft.com/Forums/en-US/4ea70f43-a0d5-43f8-8e24-78e90f208110/calling-a-function-in-a-file-from-another-file?forum=winembplatdev

Ajouter une instruction d'impression pour vérifier le résultat, ou d'utiliser echo $? après l'exécution du fichier principal

2
répondu Shameerariff 2015-02-25 10:22:53

vous pouvez l'appeler parce qu'une déclaration n'est pas nécessaire pour faire un appel en C. le type de retour est cependant inconnu, et donc par défaut à int. Ceci est possible en partie à cause de la convention d'appel par défaut en C, et de la promotion par défaut des types à au moins int précision.

si vous incluez un en-tête qui définit la fonction que vous appelez, le compilateur est en mesure de vérifier que les appels à la fonction ont le nombre et le type de argument.

si vous incluez des définitions de fonctions, elles seront exportées sauf si vous spécifiez leur stockage avec static. Puisque vous êtes aussi la compilation et la liaison add.c, vous ne pouvez pas ajouter ceci depuis lors ni l'un ni l'autre de vos fichiers objet n'exportera add.

si vous voulez simplement inclure toutes vos fonctions, mieux vaut mettre leurs définitions dans les en-têtes, et saupoudrer les spécificateurs de stockage sur eux.

1
répondu Matt Joiner 2011-07-08 15:17:27