web-dev-qa-db-fra.com

Pourquoi une fonction sans paramètre (comparée à la définition de fonction réelle) est-elle compilée?

Je viens de rencontrer le code C de quelqu'un que je ne comprends pas très bien 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ètre par rapport à la définition de fonction réelle. Deuxièmement, le paramètre dans la définition de la 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é sur quelques compilateurs, et ça fonctionne bien.

384
AdmiralJonB

Toutes les autres réponses sont correctes, mais juste pour achèvement

Une fonction est déclarée de la manière suivante:

  return-type function-name(parameter-list,...) { body... }

return-type est le type de variable renvoyé par la fonction. Cela ne peut pas être un type de tableau ou un type de fonction. Si non spécifié, alors int est supposé .

nom-fonction est le nom de la fonction.

parameter-list est la liste des paramètres que la fonction prend séparés par des virgules. Si aucun paramètre n'est fourni, la fonction n'en prend aucun et doit être définie avec un ensemble vide de parenthèses ou avec le mot clé void. Si aucun type de variable ne se trouve devant une variable dans la liste de paramètres, alors int est supposé . Les tableaux et les fonctions ne sont pas transmis aux fonctions, mais sont automatiquement convertis en pointeurs. Si la liste se termine par un Ellipsis (, ...), aucun nombre de paramètres n'est défini. Remarque: l'en-tête stdarg.h peut être utilisé pour accéder aux arguments lors de l'utilisation d'un Ellipsis.

Et encore dans un souci de complétude. D'après la spécification C11 6: 11: 6 (page: 179)

L’utilisation de déclarateurs de fonction avec des parenthèses vides (pas de déclarateurs de type de paramètre au format prototype) est une fonctionnalité obsolète .

270
Krishnabhadra

En C func() signifie que vous pouvez passer n'importe quel nombre d'arguments. Si vous ne voulez pas d'argument, vous devez le déclarer en tant que func(void). Le type que vous passez à votre fonction, s'il n'est pas spécifié, par défaut est int.

158
Tony The Lion

int func(); est une déclaration de fonction obsolète à partir des jours où il n'y avait pas de standard C, c'est-à-dire les jours de K & R C (avant 1989, année de publication du premier standard "ANSI C").

Rappelez-vous qu’il n’existait aucun prototype dans K & R C et que le mot clé void n’était pas encore inventé. Tout ce que vous pouviez faire était d'informer le compilateur du type de retour d'une fonction. La liste de paramètres vide dans K & R C signifie un nombre d'arguments "non spécifié mais fixe". Fixé signifie que vous devez appeler la fonction avec le même nombre d'arguments (à la différence d'un variadic fonctionne comme printf, où le numéro et le type peuvent varier pour chaque appel).

De nombreux compilateurs vont diagnostiquer cette construction; en particulier gcc -Wstrict-prototypes vous dira que "la déclaration de fonction n’est pas un prototype", ce qui est bien pratique, car elle ressemble à un prototype ( surtout si vous êtes empoisonné par C++!), mais ne l’est pas. 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, 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 contrôleurs statiques sont contents. Ceux qui sont induits en erreur par ^ W ^ Wfond de C++ peuvent toutefois reculer, car ils doivent saisir des caractères supplémentaires lorsqu'ils essaient d'exercer leurs compétences en langues étrangères :-)

57
Jens
  • La liste de paramètres vide signifie "tous les arguments", donc la définition n'est pas fausse.
  • Le type manquant est supposé être int.

Je considérerais que toute construction qui réussit cela manque de niveau d'avertissement/d'erreur configuré, cela ne sert à rien de permettre cela pour le code réel.

53
unwind

C'est K & R déclaration et définition de fonction de style. De la norme C99 (ISO/IEC 9899: TC3)

Section 6.7.5.3 Déclarateurs de fonctions (y compris les prototypes)

Une liste d'identifiants ne déclare que les identifiants des paramètres de la fonction. Une liste vide dans un déclarateur de fonction qui fait partie d'une définition de cette fonction spécifie que la fonction n'a pas de paramètre. La liste vide dans un déclarateur de fonction qui ne fait pas partie d'une définition de cette fonction spécifie qu'aucune information sur le nombre ou les types de paramètres n'est fournie. (Si les deux types de fonctions sont "ancien style", les types de paramètres sont: non comparé.)

Section 6.11.6 Déclarateurs de fonction

L'utilisation de déclarateurs de fonction avec des parenthèses vides (et non des déclarateurs de type de paramètre au format prototype) est un fonctionnalité obsolète.

Section 6.11.7 Définitions des fonctions

L'utilisation de définitions de fonction avec un identificateur de paramètre et des listes de déclaration distincts (et non des déclarateurs de type de paramètre au format prototype) est un fonctionnalité obsolète.

Que signifie l'ancien style K & R style

Exemple:

Déclaration: int old_style();

Définition:

int old_style(a, b)
    int a; 
    int b;
{
     /* something to do */
}
29
Lei Mou

C suppose int si aucun type n'est donné pour le type de retour de fonction et la liste de paramètres. Ce n'est que pour cette règle que des choses étranges sont possibles.

Une définition de fonction ressemble à ceci.

int func(int param) { /* body */}

Si c'est un prototype, vous écrivez

int func(int param);

En prototype, vous ne pouvez spécifier que le type de paramètres. Le nom des paramètres n'est pas obligatoire. Alors

int func(int);

De même, si vous ne spécifiez pas le type de paramètre mais que le nom int est considéré comme type.

int func(param);

Si vous allez plus loin, suivre fonctionne aussi.

func();

Le compilateur suppose que int func() lorsque vous écrivez func(). Mais ne mettez pas func() à l'intérieur d'un corps de fonction. Ce sera un appel de fonction

15
Shiplu Mokaddim

Comme indiqué par @Krishnabhadra, toutes les réponses précédentes d'autres utilisateurs ont une interprétation correcte et je souhaite simplement analyser plus en détail certains points.

Dans l’ancien C comme dans ANSI-C, le "paramètre formel non typé", prenez la dimension de votre registre de travail ou de votre capacité de profondeur d’instruction (registres fantômes ou cycle cumulatif d’instructions), dans une MPU 8 bits, être un int16, dans un MPU 16 bits et sera donc un int16 et ainsi de suite, dans le cas où les architectures 64 bits peuvent choisir de compiler des options telles que: -m32.

Bien que la mise en œuvre semble plus simple à haut niveau, pour le passage de plusieurs paramètres, le travail du programmeur dans l'étape de type de données de dimension de contrôle 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 à l'intérieur ou à l'extérieur du registre de travail. presque la même chose avec l'utilisation de "volatile" et "registre".

Mais il convient de 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:

main.c

main2.c

main3.c 
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, générez un type de données prototype déclaratif.

Comme ça:

int myfunc(int param);
11
RTOSkit

En ce qui concerne le type de paramètre, il existe déjà des réponses correctes ici, mais si vous voulez que le compilateur l’entende, vous pouvez essayer d’ajouter des indicateurs (les indicateurs sont de toute façon presque toujours une bonne idée).

compiler votre programme en utilisant gcc foo.c -Wextra je reçois:

foo.c: In function ‘func’:
foo.c:5:5: warning: type of ‘param’ defaults to ‘int’ [-Wmissing-parameter-type]

étrangement -Wextra ne comprend pas cela pour clang (il ne reconnaît pas -Wmissing-parameter-type pour une raison quelconque, peut-être pour les raisons historiques mentionnées ci-dessus), mais -pedantic le fait:

foo.c:5:10: warning: parameter 'param' was not declared, 
defaulting to type 'int' [-pedantic]
int func(param)
         ^
1 warning generated.

Et pour le problème du prototype, comme indiqué à nouveau ci-dessus, int func() fait référence à des paramètres arbitraires, à moins que vous ne le définissiez explicitement comme int func(void), ce qui vous donnerait alors les erreurs attendues:

foo.c: In function ‘func’:
foo.c:6:1: error: number of arguments doesn’t match prototype
foo.c:3:5: error: prototype declaration
foo.c: In function ‘main’:
foo.c:12:5: error: too many arguments to function ‘func’
foo.c:5:5: note: declared here

ou dans clang comme:

foo.c:5:5: error: conflicting types for 'func'
int func(param)
    ^
foo.c:3:5: note: previous declaration is here
int func(void);
    ^
foo.c:12:20: error: too many arguments to function call, expected 0, have 1
    int bla = func(10);
              ~~~~ ^~
foo.c:3:1: note: 'func' declared here
int func(void);
^
2 errors generated.
5
none

Si la déclaration de fonction n’a pas de paramètre, c’est-à-dire vide, elle prend un nombre non spécifié d’arguments. Si vous voulez que cela ne prenne aucun argument, changez le en:

int func(void);
3
P.P.

C'est pourquoi je conseille généralement aux personnes de compiler leur code avec:

cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition

Ces drapeaux imposent plusieurs choses:

  • -Wmissing-variable-declarations: Il est impossible de déclarer une fonction non statique sans obtenir un prototype au préalable. Cela rend plus probable qu'un prototype dans un fichier d'en-tête corresponde à la définition réelle. Autrement, vous devez ajouter le mot clé static à des fonctions qui n'ont pas besoin d'être visibles publiquement.
  • -Wstrict-variable-declarations: le prototype doit lister correctement les arguments.
  • -Wold-style-definition: la définition de fonction elle-même doit également lister correctement les arguments.

Ces indicateurs sont également utilisés par défaut dans de nombreux projets Open Source. Par exemple, FreeBSD a activé ces indicateurs lors de la compilation avec WARNS = 6 dans votre Makefile.

0
Ed Schouten