Je viens de rencontrer le code C de quelqu'un que je ne comprends pas pourquoi il compile. Il y a deux points que je ne comprends pas.
Tout d'abord, le prototype de fonction n'a pas de paramètres par rapport à la définition de fonction réelle. Deuxièmement, le paramètre dans la définition de fonction n'a pas de type.
#include <stdio.h>
int func();
int func(param)
{
return param;
}
int main()
{
int bla = func(10);
printf("%d", bla);
}
Pourquoi ça marche? Je l'ai testé dans quelques compilateurs, et cela fonctionne très bien.
c
parameters
function-prototypes
function-parameter
AdmiralJonB
la source
la source
-Wstrict-prototypes
fois pourint func()
etint main()
: xc: 3: avertissement: la déclaration de fonction n'est pas un prototype. Vous devez déclarermain()
commemain(void)
aussi bien.int func();
est compatible avecint func(arglist) { ... }
.int main(void)
.Réponses:
Toutes les autres réponses sont correctes, mais juste pour terminer
Et encore une fois par souci d'exhaustivité. De la spécification C11 6: 11: 6 (page: 179)
la source
En C
func()
signifie que vous pouvez passer n'importe quel nombre d'arguments. Si vous ne voulez aucun argument, vous devez déclarer en tant quefunc(void)
. Le type que vous transmettez à votre fonction, s'il n'est pas spécifié par défaut, estint
.la source
func(42,0x42);
(où tous les appels doivent utiliser le formulaire à deux arguments).unsigned
est juste un autre nom pour le même type queunsigned int
.int func();
est une déclaration de fonction obsolète des jours où il n'y avait pas de norme C, c'est-à-dire les jours de K&R C (avant 1989, l'année de publication de la première norme "ANSI C").N'oubliez pas qu'il n'y avait pas de prototypes dans K&R C et que le mot-clé
void
n'était pas encore inventé. Tout ce que vous pouviez faire était d'indiquer au compilateur le type de retour d'une fonction. La liste de paramètres vide dans K&R C signifie "un nombre non spécifié mais fixe" d'arguments. Fixe signifie que vous devez appeler la fonction avec le même nombre d'arguments à chaque fois (par opposition à une fonction variadique commeprintf
, où le nombre et le type peuvent varier pour chaque appel).De nombreux compilateurs diagnostiqueront cette construction; en particulier
gcc -Wstrict-prototypes
, vous dira que "la déclaration de fonction n'est pas un prototype", ce qui est parfait, car il semble à un prototype (surtout si vous êtes empoisonné par C ++!), mais ce n'est pas le cas. Il s'agit d'une déclaration de type de retour K&R C à l'ancienne.Règle générale: ne laissez jamais une déclaration de liste de paramètres vide vide, utilisez
int func(void)
pour être spécifique. Cela transforme la déclaration de type de retour K&R en un prototype C89 approprié. Les compilateurs sont contents, les développeurs sont contents, les vérificateurs statiques sont contents. Ceux qui sont trompés par ^ W ^ Wfond de C ++ peuvent grincer des dents, car ils doivent taper des caractères supplémentaires lorsqu'ils essaient d'exercer leurs compétences en langues étrangères :-)la source
int
.Je considérerais que toute construction qui passe cela manque dans le niveau d'avertissement / d'erreur configuré, il n'y a aucun intérêt à autoriser le code réel.
la source
C'est la déclaration et la définition de la fonction de style K&R . De la norme C99 (ISO / IEC 9899: TC3)
Section 6.7.5.3 Déclarateurs de fonctions (y compris les prototypes)
Section 6.11.6 Déclarateurs de fonctions
Section 6.11.7 Définitions des fonctions
Ce qui signifie que l'ancien style K & R de style
Exemple:
Déclaration:
int old_style();
Définition:
la source
C suppose que
int
si aucun type n'est donné sur le type de retour de fonction et la liste des paramètres . Ce n'est que pour cette règle que des choses étranges sont possibles.Une définition de fonction ressemble à ceci.
Si c'est un prototype que vous écrivez
Dans le prototype, vous ne pouvez spécifier que le type de paramètres. Le nom des paramètres n'est pas obligatoire. Donc
De plus, si vous ne spécifiez pas le type de paramètre mais que le nom
int
est supposé comme type.Si vous allez plus loin, la suite fonctionne aussi.
Le compilateur suppose
int func()
que vous écrivezfunc()
. Mais ne mettez pas à l'func()
intérieur d'un corps de fonction. Ce sera un appel de fonctionla source
int func()
n'est pas une forme implicite deint func(int)
.Comme indiqué @Krishnabhadra, toutes les réponses précédentes des autres utilisateurs ont une interprétation correcte, et je veux juste faire une analyse plus détaillée de certains points.
Dans l'ancien C comme dans l'ANSI-C, le " paramètre formel non typé ", prenez la dimension de votre registre de travail ou capacité de profondeur d'instruction (registres fantômes ou cycle cumulatif d'instructions), dans une MPU 8 bits, sera un int16, dans une 16 bits MPU et ainsi sera un int16 et ainsi de suite, dans le cas où les architectures 64 bits peuvent choisir de compiler des options comme: -m32.
Bien qu'il semble plus simple de l'implémentation à haut niveau, Pour passer plusieurs paramètres, le travail du programmeur dans l'étape de type de données de contrôle dimencion, devient plus exigeant.
Dans d'autres cas, pour certaines architectures de microprocesseurs, les compilateurs ANSI personnalisés, ont exploité certaines de ces anciennes fonctionnalités pour optimiser l'utilisation du code, forçant l'emplacement de ces "paramètres formels non typés" à fonctionner dans ou en dehors du registre de travail, aujourd'hui vous obtenez presque la même chose avec l'utilisation de "volatile" et de "registre".
Mais il faut noter que les compilateurs les plus modernes, ne font aucune distinction entre les deux types de déclaration de paramètres.
Exemples de compilation avec gcc sous linux:
Dans tous les cas, la déclaration du prototype localement ne sert à rien, car il n'y a pas d'appel sans paramètres, la référence à ce prototype sera négligente. Si vous utilisez le système avec "paramètre formel non typé", pour un appel externe, procédez à la génération d'un type de données prototype déclaratif.
Comme ça:
la source
int
, qui est généralement soit la taille du registre de travail, soit 16 bits, selon le plus petit.int
type qui n'était pas capable à la fois de contenir toutes les valeurs de la plage - 32767 à +32767 (la note -32768 n'est pas requise) et peut également contenir toutes leschar
valeurs (ce qui signifie que sichar
est 16 bits, il doit être signé ouint
doit être plus grand).Concernant le type de paramètre, il y a déjà des réponses correctes ici, mais si vous voulez l'entendre du compilateur, vous pouvez essayer d'ajouter des indicateurs (les indicateurs sont presque toujours une bonne idée de toute façon).
compiler votre programme en utilisant
gcc foo.c -Wextra
J'obtiens:Étrangement,
-Wextra
n'attrape pas celaclang
(il ne reconnaît pas-Wmissing-parameter-type
pour une raison quelconque, peut-être pour les éléments historiques mentionnés ci-dessus) mais le-pedantic
fait:Et pour le problème de prototype, comme indiqué ci-dessus, se
int func()
réfère à des paramètres arbitraires à moins que vous ne le définissiez de manière exclusive commeint func(void)
ce qui vous donnerait alors les erreurs comme prévu:ou en
clang
tant que:la source
Si la déclaration de fonction n'a pas de paramètres, c'est-à-dire vide, alors elle prend un nombre non spécifié d'arguments. Si vous voulez qu'il ne prenne aucun argument, changez-le en:
la source
C'est pourquoi je conseille généralement aux gens de compiler leur code avec:
Ces indicateurs renforcent deux ou trois choses:
Ces indicateurs sont également utilisés par défaut dans de nombreux projets Open Source. Par exemple, FreeBSD a ces drapeaux activés lors de la construction avec WARNS = 6 dans votre Makefile.
la source