Pourquoi auto a = 1; compiler en C?

125

Le code:

int main(void)
{
    auto a=1;
    return 0;
}

est compilé sans erreur par le compilateur MS Visual Studio 2012, lorsque le fichier porte l'extension .c. J'ai toujours pensé que lorsque vous utilisez l'extension .c, la compilation doit être conforme à la syntaxe C et non à C ++. De plus, pour autant que je sache, auto sans type n'est autorisé qu'en C ++ depuis C ++ 11, où cela signifie que le type est déduit de l'initialiseur.

Cela signifie-t-il que mon compilateur ne s'en tient pas à C, ou le code est-il réellement correct en langage C?

lee77
la source
8
Soit vous compilez en mode C ++ (possible), soit MS est toujours bloqué dans le dernier millénaire. Implicite inta été supprimé de la norme C en 1999.
Jens Gustedt
16
@JensGustedt MSVC ++ ne prend en charge que C89 (et quelques fonctionnalités de C99). Il s'agit plus d'un compilateur C ++.
ntoskrnl
3
@Brandin: Avec l'option / Wall, cela donne l'avertissement C4431, indiquant qu'un spécificateur de type est manquant et que l'int par défaut n'est plus pris en charge en C (voir le commentaire de Jens). C'est un peu une contradiction puisque évidemment ce compilateur le supporte ...
lee77
4
@JensGustedt Par cette mesure, GCC 4.7, sorti en 2012, (et les versions ultérieures aussi, je suppose - je ne les ai pas sous la main) est également "coincé dans le dernier millénaire". Il compile le code d'OP sans même un préavis lorsqu'il ne reçoit aucun drapeau.
3
@delnan, je supposais au moins que l'OP avait activé les niveaux d'avertissement. J'avais manifestement tort. Et dans un sens, c'est vrai, gcc est toujours bloqué là-bas, car ils n'ont toujours pas C99 (ou une variante) par défaut. clang met en garde contre la construction, même sans drapeaux.
Jens Gustedt

Réponses:

240

autoest un ancien mot-clé C qui signifie "portée locale". auto aest identique à auto int a, et comme la portée locale est la valeur par défaut pour une variable déclarée à l'intérieur d'une fonction, elle est également la même que int adans cet exemple.

Ce mot-clé est en fait un reste du prédécesseur B de C, où il n'y avait pas de types de base: tout était int, pointeur vers int, tableau de int. (*) Les déclarations seraient soit autoou extrn[sic]. C a hérité du "tout est int" comme règle par défaut, vous pouvez donc déclarer des entiers avec

auto a;
extern b;
static c;

ISO C s'est débarrassé de cela, mais de nombreux compilateurs l'acceptent toujours pour la compatibilité descendante. Si cela ne vous semble pas familier, vous devez vous rendre compte qu'une règle connexe est à l'œuvre dans

unsigned d;  // actually unsigned int

ce qui est encore courant dans le code moderne.

C ++ 11 a réutilisé le mot-clé, que peu ou pas de programmeurs C ++ utilisaient avec la signification d'origine, pour son inférence de type. Ceci est généralement sûr car la intrègle «tout est » de C avait déjà été supprimée dans C ++ 98; la seule chose qui casse auto T a, c'est que personne n'utilisait de toute façon. (Quelque part dans ses articles sur l'histoire de la langue , Stroustrup commente cela, mais je ne trouve pas la référence exacte pour le moment.)

(*) La gestion des chaînes en B était intéressante: vous utiliseriez des tableaux intet regrouperiez plusieurs caractères dans chaque membre. B était en fait BCPL avec une syntaxe différente.

Fred Foo
la source
7
Non, ce n'est pas un C légal depuis 1999. Aucun compilateur C moderne décent ne le permet.
Jens Gustedt
18
@JensGustedt VS ne prétend pas fournir un compilateur C moderne. De toute apparence, le travail sur le compilateur C s'est arrêté il y a plusieurs années; ils ne le fournissent que pour que les gens puissent continuer à compiler le code hérité. (Et bien sûr, tout compilateur C moderne et décent aura des options pour prendre en charge le code hérité. Y compris une option pour K&R C.)
James Kanze
23
@JensGustedt: êtes-vous sûr? GCC et Clang en avertissent tous les deux en mode C99, mais ils ne le considèrent pas comme une erreur sauf avec -Werror.
Fred Foo
2
@larsman, oui, dans 6.7.2 il y a une contrainte explicite pour cela: au moins un spécificateur de type doit être donné dans les spécificateurs de déclaration dans chaque déclaration ...
Jens Gustedt
40
@JensGustedt - re Non, ce n'est pas un C légal depuis 1999. Aucun compilateur C moderne décent ne le permet. La première affirmation est correcte; il est illégal depuis 1999. À mon humble avis, la deuxième déclaration est incorrecte. Tout compilateur C moderne et décent doit permettre cela. Regardez tout le code hérité qui devrait être réécrit s'il ne le permettait pas. J'ai écrit une réponse qui développe ce commentaire.
David Hammen
35

C'est à la fois une réponse et un commentaire étendu à Non, ce n'est pas légal depuis 1999. Aucun compilateur C moderne décent ne le permet.

Oui, auto a=1;est illégal dans C1999 (et aussi C2011). Ce n'est pas parce que c'est maintenant illégal qu'un compilateur C moderne devrait rejeter le code qui contient de telles constructions. Je dirais exactement le contraire, qu'un compilateur C moderne et décent doit encore permettre cela.

Clang et gcc font exactement cela lors de la compilation de l'exemple de code dans la question par rapport aux versions 1999 ou 2011 de la norme. Les deux compilateurs émettent un diagnostic puis continuent comme si la déclaration répréhensible avait été auto int a=1;.

À mon avis, c'est ce qu'un compilateur décent devrait faire. En émettant un diagnostic, clang et gcc sont entièrement conformes à la norme. La norme ne dit pas qu'un compilateur doit rejeter le code illégal. La norme dit simplement qu'une implémentation conforme doit produire au moins un message de diagnostic si une unité de traduction contient une violation d'une règle de syntaxe ou d'une contrainte (5.1.1.3).

Étant donné le code qui contient des constructions illégales, tout compilateur décent essaiera de donner un sens au code illégal afin que le compilateur puisse trouver l'erreur suivante dans le code. Un compilateur qui s'arrête à la première erreur n'est pas un très bon compilateur. Il existe un moyen de donner un sens à auto a=1, qui consiste à appliquer la règle "implicit int". Cette règle force le compilateur à interpréter auto a=1comme si c'était le cas auto int a=1lorsque le compilateur est utilisé en mode C90 ou K&R.

La plupart des compilateurs rejettent généralement le code (rejeter: refuser de générer un fichier objet ou un exécutable) qui contient une syntaxe illégale. C'est un cas où les auteurs du compilateur ont décidé que ne pas compiler n'était pas la meilleure option. La meilleure chose à faire est d'émettre un diagnostic, de corriger le code et de continuer. Il y a trop de code hérité parsemé de constructions telles que register a=1;. Le compilateur devrait être capable de compiler ce code en mode C99 ou C11 (avec un diagnostic, bien sûr).

David Hammen
la source
1
@larsmans - Je peux voir d'où vous venez. Vous voulez une -ffs-please-stop-allowing-constructs-from-some-previous-millenniumoption de compilateur, ou plus succinctement, une -fstrict-complianceoption. Grommelant au compilateur: "Quand j'ai utilisé -std = c11, je ne m'attendais pas à ce que l'ancien kruft K&R compile. En fait, je voulais qu'il ne compile pas !"
David Hammen
1
En fait , non, je veux avoir à tourner sur un drapeau pour obtenir le pire cochonneries à la compilation. Mais -std=c99être plus strict serait un pas dans la bonne direction :)
Fred Foo
1
Si vous utilisez gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror(ce que j'utilise régulièrement, même sur le code des questions sur SO), alors vous vous rapprochez assez de ce que vous voulez. J'aimerais que GCC soit par défaut au moins -std=c99et de préférence -std=c11(ou -std=gnu11,; ils le feraient plus probablement), mais d'ici là, ... Vous pouvez modifier ces options; -pedantic, -Wshadow, -Wold-style-declarationEt d'autres peuvent être utiles, mais cela est un bon départ ensemble d'options.
Jonathan Leffler
3
@DavidHammen: Ni la complexité cyclomatique, ni les chefs de projet, ni les politiques de l'entreprise ne sont des éléments du langage.
Jerry B
3
Le drapeau pour obtenir le comportement souhaité dans GCC est le-pedantic-errors
τεκ
29

autoa une signification dans Cet C++avant la Norme 2011. Cela signifie qu'une variable a une durée de vie automatique, c'est-à-dire une durée de vie déterminée par la portée . Ceci est opposé, par exemple, à la staticdurée de vie, où une variable dure "pour toujours", quelle que soit la portée. autoest la durée de vie par défaut, et n'est presque jamais explicite. C'est pourquoi il était prudent de changer le sens C++.

Désormais C, avant la norme 99, si vous ne spécifiez pas le type d'une variable, la valeur par défaut est int.

Donc, avec auto a = 1;vous déclarez (et définissez) une intvariable, avec une durée de vie déterminée par la portée.

(«durée de vie» est plus correctement appelée «durée de stockage», mais je pense que c'est peut-être moins clair).

BoBTFish
la source
Ok, donc en fait auto a = 1 est autorisé en C et signifie une variable int avec une durée de stockage automatique.
lee77
1
Correctement, "durée de stockage" prend l'une d'une liste de valeurs, "automatique", "statique", "dynamique", "thread". "Lifetime" est la durée de vie réelle de l'objet. Ainsi, la variable a une durée de stockage "automatique" et une durée de vie "la durée de la portée de la mainfonction".
Steve Jessop
@Steve oui, je ne voulais pas le laisser entendre autoet ce staticsont les deux seules possibilités. J'essayais d'écrire ma réponse d'une manière ciblée sur le demandeur, qui semble être assez nouveau pour C++(et C), alors j'ai un peu passé sous silence les détails. C'était peut-être une mauvaise idée; ils doivent être couverts tôt ou tard.
BoBTFish
1
@BoBTFish: oh, je ne m'en plaignais pas. Je voulais juste m'étendre sur la différence sémantique entre «durée de vie», qui est une durée, et «durée de stockage» qui aurait pu être plus précisément appelée «catégorie de durée de stockage».
Steve Jessop
Ce inttruc implicite est supprimé de C depuis 1999.
Jens Gustedt
8

En C, et les dialectes historiques de C ++, il autoy a un mot-clé signifiant qui aa un stockage automatique. Comme il ne peut être appliqué qu'aux variables locales, qui sont automatiques par défaut, personne ne l'utilise; c'est pourquoi C ++ a maintenant réutilisé le mot-clé.

Historiquement, C a autorisé les déclarations de variables sans spécificateur de type; le type par défaut est int. Cette déclaration équivaut donc à

int a=1;

Je pense que c'est déconseillé (et peut-être interdit) dans le C moderne; mais certains compilateurs populaires utilisent par défaut C90 (ce qui, je pense, le permet), et, ce qui est ennuyeux, n'active les avertissements que si vous les demandez spécifiquement. Compiler avec GCC et spécifier C99 avec -std=c99, ou activer l'avertissement avec -Wallou -Wimplicit-int, donne un avertissement:

warning: type defaults to int in declaration of a
Mike Seymour
la source
4
Il est en effet interdit en C depuis 1999.
Jens Gustedt
5

En C, cela autosignifie la même chose registeren C ++ 11: cela signifie qu'une variable a une durée de stockage automatique.

Et dans C avant C99 (et le compilateur de Microsoft ne prend en charge ni C99 ni C11, bien qu'il puisse en prendre en charge certaines parties), le type peut être omis dans de nombreux cas, où il sera par défaut int.

Il ne prend pas du tout le type de l'initialiseur. Vous venez de choisir un initialiseur compatible.


la source
1
Le mot clé register n'est-il pas obsolète dans C ++ 11?
sordide
@sordid Oui, ça l'est. Avant C ++ 11, autoet registeravait exactement la même signification (j'ai déjà commenté qu'il y avait des restrictions sur la prise registerd'adresse d' une variable qualifiée, mais c'était incorrect pour C ++). register, bien que obsolète, conserve son ancienne signification pour le moment.
5
@JensGustedt: La réponse ne dit pas qu'ils le sont. Il dit qu'en autoC signifie la même chose qu'en registerC ++, ce qu'il fait (les deux signifient la durée de stockage automatique, et rien d'autre).
Mike Seymour
3

Le type de compilation Visual Studio est disponible sur right click on file -> Properties -> C/C++ -> Advanced -> Compile As. Pour s'assurer qu'il est compilé comme /TCoption force C. Dans ce cas, c'est ce que dit larsmans (ancien automot-clé C ). Il peut être compilé en C ++ sans que vous le sachiez.

UmNyobe
la source
3

Une classe de stockage définit la portée (visibilité) et la durée de vie des variables et / ou des fonctions au sein d'un programme C.

Les classes de stockage suivantes peuvent être utilisées dans un programme C

auto
register
static
extern

auto est la classe de stockage par défaut pour toutes les variables locales.

{
        int Count;
        auto int Month;
}

L'exemple ci-dessus définit deux variables avec la même classe de stockage. auto ne peut être utilisé que dans des fonctions, c'est-à-dire des variables locales.

intest le type par défaut pour le autocode ci-dessous:

auto Month;
/* Equals to */
int Month;

Le code ci-dessous est également légal:

/* Default-int */
main()
{
    reurn 0;
}
Amir Saniyan
la source