Почему auto a = 1; компилировать на C?

125

Код:

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

компилируется без ошибок компилятором MS Visual Studio 2012, если файл имеет расширение .c. Я всегда думал, что при использовании расширения .c компиляция должна выполняться в соответствии с синтаксисом C, а не C ++. Более того, насколько я знаю, auto без типа разрешен только в C ++, начиная с C ++ 11, где это означает, что тип выводится из инициализатора.

Означает ли это, что мой компилятор не придерживается C, или действительно ли код на C-языке правильный?

lee77
источник
8
Либо вы компилируете в режиме C ++ (возможно), либо MS все еще застряла в последнем тысячелетии. Implicit intбыл удален из стандарта C в 1999 году.
Йенс Густедт
16
@JensGustedt MSVC ++ поддерживает только C89 (и некоторые функции из C99). Это скорее компилятор C ++.
ntoskrnl
3
@Brandin: с параметром / Wall выдается предупреждение C4431, в котором говорится, что спецификатор типа отсутствует и что int по умолчанию больше не поддерживается в C (см. Комментарий Йенса). Это немного противоречит, поскольку, очевидно, этот компилятор поддерживает это ...
lee77 01
4
@JensGustedt По этому показателю GCC 4.7, выпущенный в 2012 году (и, как я подозреваю, более поздние версии тоже - у меня их нет под рукой), также «застрял в последнем тысячелетии». Он компилирует код OP без уведомления, когда ему не заданы какие-либо флаги.
3
@delnan, по крайней мере, я предполагал, что OP включил уровни предупреждений. Я явно ошибался. И в каком-то смысле это правда, gcc также по-прежнему застрял, поскольку у них по-прежнему нет C99 (или его варианта) по умолчанию. clang предупреждает о конструкции, даже без флагов.
Йенс Густедт

Ответы:

240

auto- старое ключевое слово C, означающее "локальная область видимости". auto aто же самое auto int a, что и, и поскольку локальная область видимости является значением по умолчанию для переменной, объявленной внутри функции, она также такая же, как int aв этом примере.

Это ключевое слово на самом деле осталось от предшественника C B, где не было базовых типов: все было int, указатель на int, массив int. (*) Объявления были бы либо autoили extrn[sic]. C унаследовал intправило «все есть » как правило по умолчанию, поэтому вы можете объявлять целые числа с помощью

auto a;
extern b;
static c;

ISO C избавился от этого, но многие компиляторы все еще принимают его для обратной совместимости. Если это кажется вам незнакомым, то вы должны понимать, что соответствующее правило работает в

unsigned d;  // actually unsigned int

что до сих пор распространено в современном коде.

C ++ 11 повторно использовал ключевое слово, которое мало кто из программистов C ++ использовал с исходным значением, для вывода его типа. Это в основном безопасно, потому что intправило «все есть » из C уже было исключено в C ++ 98; единственное, что ломается, это то auto T a, что все равно никто не использовал. (Где-то в своих статьях по истории языка Страуструп комментирует это, но я не могу найти точную ссылку прямо сейчас.)

(*) Обработка строк в B была интересной: вы должны использовать массивы intи упаковывать несколько символов в каждый член. На самом деле B был BCPL с другим синтаксисом.

Фред Фу
источник
7
Нет, это недопустимо для Си с 1999 года. Ни один достойный современный компилятор Си не допускает этого.
Йенс Густедт
18
@JensGustedt VS не утверждает, что предоставляет современный компилятор C. Судя по всему, работа над компилятором Си прекратилась много лет назад; они предоставляют его только для того, чтобы люди могли продолжать компилировать устаревший код. (И, конечно же, любой достойный современный компилятор C будет иметь опции для поддержки устаревшего кода. Включая опцию для K&R C.)
Джеймс Канце,
23
@JensGustedt: ты уверен? GCC и Clang предупреждают об этом в режиме C99, но не считают это ошибкой, кроме как с -Werror.
Фред Фу
2
@larsman, да, в 6.7.2 есть явное ограничение для этого: по крайней мере, один спецификатор типа должен быть указан в спецификаторах объявления в каждом объявлении ...
Йенс Густедт
40
@JensGustedt - re Нет, это недопустимо для C с 1999 года. Ни один достойный современный компилятор C не позволяет этого. Первое утверждение верно; это незаконно с 1999 года. ИМХО, второе утверждение неверно. Любой достойный современный компилятор C должен это учитывать. Посмотрите на весь унаследованный код, который пришлось бы переписать, если бы они этого не допускали. Я написал ответ, который расширяет этот комментарий.
Дэвид Хаммен
35

Это и ответ, и расширенный комментарий к « Нет», это не разрешено для C с 1999 года. Ни один достойный современный компилятор C не позволяет этого.

Да, auto a=1;это незаконно в C1999 (а также C2011). То, что это теперь незаконно, не означает, что современный компилятор C должен отклонять код, содержащий такие конструкции. Я бы сказал с точностью до наоборот, что достойный современный компилятор C все же должен позволять это.

И clang, и gcc делают это при компиляции примера кода в вопросе против версий стандарта 1999 или 2011 годов. Оба компилятора выдают диагностику, а затем продолжают работу, как если бы было ошибочное утверждение auto int a=1;.

На мой взгляд, это то, что должен делать достойный компилятор. Выполняя диагностику, clang и gcc полностью соответствуют стандарту. В стандарте не говорится, что компилятор должен отклонять недопустимый код. Стандарт просто говорит, что соответствующая реализация должна выдавать по крайней мере одно диагностическое сообщение, если единица трансляции содержит нарушение любого синтаксического правила или ограничения (5.1.1.3).

Учитывая код, содержащий недопустимые конструкции, любой достойный компилятор попытается разобраться в недопустимом коде, чтобы компилятор мог найти следующую ошибку в коде. Компилятор, который останавливается при первой ошибке, не очень хороший компилятор. Есть способ разобраться в auto a=1этом - применить правило "неявного int". Это правило заставляет компилятор интерпретировать, auto a=1как если бы он был, auto int a=1когда компилятор используется в режиме C90 или K&R.

Большинство компиляторов обычно отклоняют код (reject: отказ от создания объектного файла или исполняемого файла), который содержит недопустимый синтаксис. Это тот случай, когда авторы компилятора решили, что отказ от компиляции - не лучший вариант. Лучше всего выполнить диагностику, исправить код и продолжить. Слишком много унаследованного кода, приправленного такими конструкциями, как register a=1;. Компилятор должен уметь компилировать этот код в режиме C99 или C11 (конечно, с диагностикой).

Дэвид Хаммен
источник
1
@larsmans - я вижу, откуда вы. Вам нужен -ffs-please-stop-allowing-constructs-from-some-previous-millenniumвариант компилятора, или, говоря более кратко, -fstrict-complianceвариант. Ворчание на компилятор: «Когда я использовал -std = c11, я не ожидал, что этот древний K&R kruft будет компилироваться. На самом деле, я хотел, чтобы он не компилировался!»
Дэвид Хаммен
1
На самом деле нет , нет, я хочу, чтобы повернуть на флаг , чтобы получить худший хлам для компиляции. Но -std=c99быть строже - это шаг в правильном направлении :)
Фред Фу
1
Если вы используете gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror(это то, что я обычно использую, даже в коде из вопросов по SO), то вы довольно близко подходите к тому, что хотите. Я бы хотел, чтобы GCC по умолчанию использовал хотя бы -std=c99и предпочтительно -std=c11(или, -std=gnu11скорее всего , они так и поступили бы), но до тех пор… Вы можете настроить эти параметры; -pedantic, -Wshadow, -Wold-style-declarationИ некоторые другие могут быть полезны, но это хороший стартовый набор опций.
Джонатан Леффлер
3
@DavidHammen: Ни цикломатическая сложность, ни менеджеры проектов, ни политика компании не являются элементами языка.
Jerry B
3
Флаг для получения желаемого поведения в GCC-pedantic-errors
τεκ
29

autoимеет значение в Стандарте 2011 г. Cи C++до него. Это означает, что у переменной есть автоматическое время жизни, то есть время жизни, определяемое областью действия . Это противоположно, например, времени staticжизни, когда переменная существует «вечно», независимо от области действия. auto- время жизни по умолчанию, и почти никогда не указывается явно. Вот почему было безопасно изменить значение в C++.

Теперь C, до стандарта 99, если вы не укажете тип переменной, по умолчанию будет использоваться значение int.

Итак, auto a = 1;вы объявляете (и определяете) intпеременную, время жизни которой определяется областью видимости.

(«время жизни» правильнее называть «продолжительностью хранения», но я думаю, что это, возможно, менее ясно).

BoBTFish
источник
Итак, на самом деле auto a = 1 разрешено в C и означает переменную типа int с автоматической продолжительностью хранения.
lee77
1
Правильно, «продолжительность хранения» принимает одно из следующих значений: «автоматический», «статический», «динамический», «поток». «Время жизни» - это фактическое время жизни объекта. Таким образом, переменная имеет продолжительность хранения "автоматически" и время жизни "продолжительность действия mainфункции".
Стив Джессоп
@ Стив: да, я не имел в виду этого, autoи staticэто единственные две возможности. Я пытался написать свой ответ таким образом, чтобы он был ориентирован на спрашивающего, который, кажется, новичок в C++C), поэтому я немного замалчил детали. Может быть, это была плохая идея; они должны быть покрыты рано или поздно.
BoBTFish
1
@BoBTFish: о, я не жаловался на это. Я просто хотел расширить семантическую разницу между «временем жизни», то есть продолжительностью, и «продолжительностью хранения», которую более точно можно было бы назвать «категорией продолжительности хранения».
Стив Джессоп
Этот неявный intматериал удален из C с 1999 года.
Йенс Густедт
8

В C и исторических диалектах C ++ autoэто ключевое слово, aимеющее автоматическое хранение. Поскольку его можно применять только к локальным переменным, которые по умолчанию автоматические, никто не использует его; вот почему в C ++ это ключевое слово изменилось.

Исторически C допускал объявление переменных без спецификатора типа; по умолчанию используется тип int. Итак, это объявление эквивалентно

int a=1;

Я думаю, что это устарело (и, возможно, запрещено) в современном C; но некоторые популярные компиляторы по умолчанию используют C90 (что, я думаю, позволяет это), и, что досадно, включают предупреждения, только если вы их специально просите. Компиляция с помощью GCC и указание C99 с помощью -std=c99или включение предупреждения с помощью -Wallили -Wimplicit-intдает предупреждение:

warning: type defaults to int in declaration of a
Майк Сеймур
источник
4
Это действительно запрещено в C с 1999 года.
Йенс Густедт
5

В C autoозначает то же самое, что registerи в C ++ 11: это означает, что переменная имеет автоматическую продолжительность хранения.

А в C до C99 (а компилятор Microsoft не поддерживает ни C99, ни C11, хотя может поддерживать его части) тип во многих случаях может быть опущен, где он будет по умолчанию int.

Он вообще не берет тип из инициализатора. Вы просто случайно выбрали совместимый инициализатор.


источник
1
Разве ключевое слово register не устарело в C ++ 11?
sordid
@sordid Да, это так. До C ++ 11 autoи registerимел точно такое же значение (ранее я отмечал, что существуют ограничения на получение registerадреса -квалифицированной переменной, но это было неверно для C ++). register, хотя и не рекомендуется, на данный момент сохраняет свое старое значение.
5
@JensGustedt: Ответ не таков. Он говорит, что autoв C означает то же, что и registerв C ++, что он и делает (оба означают автоматическую продолжительность хранения и ничего больше).
Майк Сеймур,
3

Тип компиляции Visual Studio доступен по адресу right click on file -> Properties -> C/C++ -> Advanced -> Compile As. Чтобы убедиться, что он скомпилирован как /TCопция C force. Тогда в этом случае это то, что сказал ларсманс (старое autoключевое слово C ). Он может быть скомпилирован как C ++ без вашего ведома.

UmNyobe
источник
3

Класс хранения определяет область видимости и время жизни переменных и / или функций в программе C.

Существуют следующие классы хранения, которые можно использовать в программе C

auto
register
static
extern

auto - класс хранения по умолчанию для всех локальных переменных.

{
        int Count;
        auto int Month;
}

В приведенном выше примере определены две переменные с одним и тем же классом хранения. auto может использоваться только внутри функций, то есть локальных переменных.

intявляется типом по умолчанию для autoкода ниже:

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

Код ниже также допустим:

/* Default-int */
main()
{
    reurn 0;
}
Амир Саниян
источник