Dlaczego auto a = 1; skompilować w C?

125

Kod:

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

jest kompilowany bez błędów przez kompilator MS Visual Studio 2012, gdy plik ma rozszerzenie .c. Zawsze uważałem, że kiedy używasz rozszerzenia .c, kompilacja powinna być zgodna ze składnią C, a nie C ++. Ponadto, o ile wiem, auto bez typu jest dozwolone tylko w C ++ od C ++ 11, gdzie oznacza to, że typ jest wywnioskowany z inicjatora.

Czy to oznacza, że ​​mój kompilator nie trzyma się języka C, czy też kod faktycznie jest poprawny w języku C?

lee77
źródło
8
Albo kompilujesz w trybie C ++ (możliwe), albo MS wciąż tkwi w ostatnim tysiącleciu. Niejawny intzostał usunięty ze standardu C w 1999 roku.
Jens Gustedt
16
@JensGustedt MSVC ++ obsługuje tylko C89 (i kilka funkcji z C99). To bardziej kompilator C ++.
ntoskrnl
3
@Brandin: Z opcją / Wall daje ostrzeżenie C4431, mówiąc, że brakuje specyfikatora typu i że domyślna wartość int nie jest już obsługiwana w C (patrz komentarz Jensa). Trochę to sprzeczne, ponieważ oczywiście ten kompilator to obsługuje ...
lee77
4
@JensGustedt W związku z tym GCC 4.7, wydane w 2012 roku (i podejrzewam, że późniejsze wersje również - nie mam ich pod ręką) również „utknęło w ostatnim tysiącleciu”. Kompiluje kod OP nawet bez powiadomienia, gdy nie ma żadnych flag.
3
@delnan, przynajmniej przypuszczałem, że OP włączył poziomy ostrzegawcze. Najwyraźniej się myliłem. I w pewnym sensie jest to prawda, gcc również tam utknęło, ponieważ nadal nie ma domyślnie C99 (lub wariantu). clang ostrzega o konstrukcji, nawet bez flag.
Jens Gustedt

Odpowiedzi:

240

autoto stare słowo kluczowe C, które oznacza „zasięg lokalny”. auto ajest tym samym co auto int a, a ponieważ zasięg lokalny jest domyślny dla zmiennej zadeklarowanej wewnątrz funkcji, jest również taki sam jak int aw tym przykładzie.

To słowo kluczowe jest właściwie pozostałością po poprzedniku B C, gdzie nie było typów podstawowych: wszystko było int, wskaźnik do int, tablica int. (*) Deklaracje byłyby albo autoalbo extrn[sic]. C odziedziczył „wszystko jest int” jako domyślną regułę, więc możesz deklarować liczby całkowite za pomocą

auto a;
extern b;
static c;

ISO C pozbyło się tego, ale wiele kompilatorów nadal akceptuje to ze względu na kompatybilność wsteczną. Jeśli wydaje ci się to nieznane, powinieneś zdać sobie sprawę, że działa powiązana reguła

unsigned d;  // actually unsigned int

co jest nadal powszechne we współczesnym kodzie.

C ++ 11 ponownie użyło słowa kluczowego, którego niewielu programistów C ++ używało w oryginalnym znaczeniu do wnioskowania o typie. Jest to w większości bezpieczne, ponieważ intreguła „wszystko jest ” z C została już usunięta w C ++ 98; jedyną rzeczą, która się psuje, jest to auto T a, czego i tak nikt nie używał. (Gdzieś w swoich artykułach na temat historii języka Stroustrup komentuje to, ale nie mogę teraz znaleźć dokładnego odniesienia.)

(*) Obsługa łańcuchów w B była interesująca: używałbyś tablic składających się z intwielu znaków i pakował je w każdy element członkowski. B był faktycznie BCPL z inną składnią.

Fred Foo
źródło
7
Nie, to nie jest legalne od 1999 roku. Żaden przyzwoity nowoczesny kompilator C na to nie pozwala.
Jens Gustedt
18
@JensGustedt VS nie twierdzi, że dostarcza nowoczesny kompilator C. Wygląda na to, że praca nad kompilatorem C zatrzymała się wiele lat temu; dostarczają go tylko po to, aby ludzie mogli nadal kompilować starszy kod. (I oczywiście każdy przyzwoity nowoczesny kompilator C będzie miał opcje obsługi starszego kodu. W tym opcja dla K&R C.)
James Kanze
23
@JensGustedt: czy na pewno? GCC i Clang ostrzegają o tym w trybie C99, ale nie uważają tego za błąd z wyjątkiem -Werror.
Fred Foo
2
@larsman, tak, w 6.7.2 istnieje wyraźne ograniczenie: co najmniej jeden specyfikator typu powinien być podany w specyfikatorach deklaracji w każdej deklaracji ...
Jens Gustedt
40
@JensGustedt - re Nie, to nie jest legalne od 1999 roku. Żaden przyzwoity nowoczesny kompilator C na to nie pozwala. Pierwsze stwierdzenie jest poprawne; jest nielegalne od 1999 roku. IMHO, drugie stwierdzenie jest niepoprawne. Każdy przyzwoity nowoczesny kompilator C musi na to pozwolić. Spójrz na cały starszy kod, który musiałby zostać przepisany, gdyby na to nie pozwolili. Napisałem odpowiedź, która rozszerza ten komentarz.
David Hammen
35

To jest zarówno odpowiedź, jak i rozszerzony komentarz do Nie, to nie jest legalne od 1999 roku. Żaden przyzwoity nowoczesny kompilator C na to nie pozwala.

Tak, auto a=1;jest nielegalne w C1999 (a także w C2011). To, że jest to teraz nielegalne, nie oznacza, że ​​nowoczesny kompilator C powinien odrzucać kod zawierający takie konstrukcje. Twierdziłbym dokładnie odwrotnie, że przyzwoity, nowoczesny kompilator C musi na to nadal pozwalać.

Zarówno clang, jak i gcc robią to właśnie podczas kompilowania przykładowego kodu w pytaniu względem wersji standardu z 1999 lub 2011 roku. Obaj kompilatorzy wydają diagnostykę, a następnie kontynuują tak, jakby było to stwierdzenie budzące zastrzeżenia auto int a=1;.

Moim zdaniem to właśnie powinien robić porządny kompilator. Wydając diagnostykę, clang i gcc są w pełni zgodne ze standardem. Standard nie mówi, że kompilator musi odrzucać nielegalny kod. Norma mówi jedynie, że zgodna implementacja musi generować co najmniej jeden komunikat diagnostyczny, jeśli jednostka tłumacząca zawiera naruszenie jakiejkolwiek reguły składniowej lub ograniczenia (5.1.1.3).

Biorąc pod uwagę kod, który zawiera niedozwolone konstrukcje, każdy przyzwoity kompilator spróbuje zrozumieć niedozwolony kod, aby kompilator mógł znaleźć następny błąd w kodzie. Kompilator, który zatrzymuje się przy pierwszym błędzie, nie jest zbyt dobrym kompilatorem. Istnieje sposób, aby wyciągnąć z tego sens auto a=1, który polega na zastosowaniu reguły „implicit int”. Ta reguła wymusza na kompilatorze interpretację auto a=1tak, jakby miała miejsce, auto int a=1gdy kompilator jest używany w trybie C90 lub K&R.

Większość kompilatorów zazwyczaj odrzuca kod (odrzucenie: odmowa wygenerowania pliku obiektowego lub pliku wykonywalnego), który zawiera niedozwoloną składnię. Jest to przypadek, w którym autorzy kompilatora zdecydowali, że niepowodzenie kompilacji nie jest najlepszą opcją. Najlepiej przeprowadzić diagnostykę, naprawić kod i kontynuować. Jest po prostu zbyt wiele starszego kodu, który jest usiany konstrukcjami takimi jak register a=1;. Kompilator powinien móc skompilować ten kod w trybie C99 lub C11 (oczywiście z diagnostyką).

David Hammen
źródło
1
@larsmans - widzę, skąd pochodzisz. Potrzebujesz -ffs-please-stop-allowing-constructs-from-some-previous-millenniumopcji kompilatora lub, bardziej zwięźle, -fstrict-complianceopcji. Narzekanie na kompilator: „Kiedy użyłem -std = c11, nie spodziewałem się, że starożytny kruft K&R się skompiluje. Tak naprawdę chciałem, żeby się nie kompilował!”
David Hammen
1
Właściwie nie chcę trzeba skręcić na flagę, aby uzyskać najgorszy cruft skompilować. Ale -std=c99bycie bardziej restrykcyjnym byłoby krokiem we właściwym kierunku :)
Fred Foo
1
Jeśli używasz gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror(czego używam rutynowo, nawet w kodzie z pytań na SO), wtedy zbliżasz się do tego, czego chcesz. Chciałbym, aby GCC domyślnie ustawiało przynajmniej -std=c99i najlepiej -std=c11(lub, -std=gnu11bardziej prawdopodobne jest, że to zrobią), ale do tego czasu… Możesz modyfikować te opcje; -pedantic, -Wshadow, -Wold-style-declarationA inni mogą być przydatne, ale jest to dobry Zaczynając zestaw opcji.
Jonathan Leffler
3
@DavidHammen: Ani złożoność cykliczna, kierownicy projektów, ani polityka firmy nie są elementami języka.
Jerry B
3
Flaga umożliwiająca uzyskanie żądanego zachowania w GCC to-pedantic-errors
τεκ
29

automa znaczenie zawarte w standardzie z 2011 r. Ci C++przed nim. Oznacza to, że zmienna ma automatyczny czas życia, czyli czas życia określony przez zakres . Jest to przeciwieństwo np. staticCzasu życia, w którym zmienna trwa „wiecznie”, niezależnie od zakresu. autojest domyślnym okresem życia i prawie nigdy nie jest wyraźnie podawany. Dlatego zmiana znaczenia w C++.

Teraz C, przed standardem 99, jeśli nie określisz typu zmiennej, domyślnie jest to int.

Więc razem z auto a = 1;tobą deklarujesz (i definiujesz) intzmienną, której czas życia jest określony przez zakres.

(„żywotność” jest bardziej poprawnie nazywana „czasem przechowywania”, ale myślę, że jest to mniej jasne).

BoBTFish
źródło
Okay, więc w rzeczywistości auto a = 1 jest dozwolone w C i oznacza zmienną int z automatycznym czasem przechowywania.
lee77
1
Właściwie „czas przechowywania” przyjmuje jedną z list wartości: „automatyczny”, „statyczny”, „dynamiczny”, „wątek”. „Czas życia” to rzeczywisty czas życia obiektu. Tak więc zmienna ma czas trwania „automatyczny” i czas życia „czas trwania zakresu mainfunkcji”.
Steve Jessop
@Steve tak, nie chciałem tego sugerować autoi staticsą to jedyne dwie możliwości. Próbowałem napisać odpowiedź w sposób skierowany do pytającego, który wydaje się być całkiem nowy dla C++(i C), więc trochę przemilczałem szczegóły. Może to był zły pomysł; wcześniej czy później trzeba je pokryć.
BoBTFish
1
@BoBTFish: och, nie narzekałem. Chciałem po prostu rozwinąć semantyczną różnicę między „okresem życia”, który jest czasem trwania, a „czasem przechowywania”, który można by dokładniej nazwać „kategorią czasu przechowywania”.
Steve Jessop
Ta ukryta inttreść jest usuwana z C od 1999 roku.
Jens Gustedt
8

W C i historycznych dialektach C ++ autojest słowem kluczowym oznaczającym aautomatyczne przechowywanie. Ponieważ można go zastosować tylko do zmiennych lokalnych, które są domyślnie automatyczne, nikt go nie używa; dlatego C ++ zmienił teraz przeznaczenie słowa kluczowego.

Historycznie rzecz biorąc, C dopuszczał deklaracje zmiennych bez specyfikatora typu; domyślny typ to int. Więc ta deklaracja jest równoważna

int a=1;

Myślę, że jest to przestarzałe (i prawdopodobnie zabronione) w nowoczesnym C; ale niektóre popularne kompilatory domyślnie używają C90 (co, jak sądzę, na to zezwala) i, irytująco, włączają ostrzeżenia tylko wtedy, gdy specjalnie o nie poprosisz. Kompilacja z GCC i podanie C99 za pomocą -std=c99lub włączenie ostrzeżenia za pomocą -Walllub -Wimplicit-intdaje ostrzeżenie:

warning: type defaults to int in declaration of a
Mike Seymour
źródło
4
Jest to rzeczywiście zabronione w C od 1999 roku.
Jens Gustedt
5

W C autooznacza to samo, co registerw C ++ 11: oznacza, że ​​zmienna ma automatyczny czas trwania.

A w C przed C99 (a kompilator Microsoftu nie obsługuje ani C99, ani C11, chociaż może obsługiwać ich części), typ można pominąć w wielu przypadkach, w których będzie domyślnie int.

W ogóle nie pobiera typu z inicjatora. Po prostu przypadkiem wybrałeś kompatybilny inicjator.


źródło
1
Czy słowo kluczowe register nie jest przestarzałe w C ++ 11?
obskurny
@sordid Tak, jest. Przed C ++ 11 autoi registermiał dokładnie to samo znaczenie (wcześniej komentowałem, że istnieją ograniczenia w pobieraniu registeradresu zmiennej -kwalifikowanej, ale było to niepoprawne dla C ++). register, choć przestarzałe, zachowuje na razie swoje stare znaczenie.
5
@JensGustedt: Odpowiedź nie mówi, że tak. Mówi, że autow C oznacza to samo co registerw C ++, co robi (oba oznaczają automatyczny czas przechowywania i nic więcej).
Mike Seymour
3

Typ kompilacji programu Visual Studio jest dostępny pod adresem right click on file -> Properties -> C/C++ -> Advanced -> Compile As. Aby upewnić się, że jest skompilowana jako /TCopcja wymuszona w C, w tym przypadku tak właśnie powiedział larsmans (stare autosłowo kluczowe C ). Może zostać skompilowany jako C ++ bez Twojej wiedzy.

UmNyobe
źródło
3

Klasa pamięci definiuje zakres (widoczność) i czas życia zmiennych i / lub funkcji w programie w C.

Istnieją następujące klasy pamięci, których można używać w programie C.

auto
register
static
extern

auto to domyślna klasa pamięci dla wszystkich zmiennych lokalnych.

{
        int Count;
        auto int Month;
}

Powyższy przykład definiuje dwie zmienne z tą samą klasą pamięci. auto może być używane tylko w funkcjach, tj. zmiennych lokalnych.

intjest domyślnym typem autow poniższym kodzie:

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

Poniższy kod jest również legalny:

/* Default-int */
main()
{
    reurn 0;
}
Amir Saniyan
źródło