#c #gcc #c99 #c89 #strtoull
#c #gcc #c99 #c89 #strtoull
Вопрос:
Я прочитал документацию для strtoul() / strtoull() отсюда, и в разделе «Соответствие» снизу вверх, он делает эти два пункта:
strtoul(): POSIX.1-2001, POSIX.1-2008, C89, C99 SVr4.
strtoull(): POSIX.1-2001, POSIX.1-2008, C99.
Эти две строки, в дополнение к другим ссылкам по всему документу, указывают мне, что функция strtoull не должна быть доступна при компиляции программы с использованием стандарта c89 / c90. Однако, когда я запускаю быстрый тест с помощью gcc, он позволяет мне вызывать эту функцию независимо от стандарта, который я указываю.
Во-первых, код, который я использую для тестирования:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
unsigned long long x;
const char *str = "1234";
x = strtoull(str, NULL, 10);
printf("%llun", x);
return 0;
}
И вот моя команда компиляции:
gcc test.c -std=c89 -pedantic -Wall -Wextra
Теперь, честно говоря, это предупреждает меня о проблеме совместимости:
test.c: In function ‘main’:
test.c:6:16: warning: ISO C90 does not support ‘long long’ [-Wlong-long]
unsigned long long x;
^~~~
test.c:9:6: warning: implicit declaration of function ‘strtoull’; did you mean ‘strtoul’? [-Wimplicit-function-declaration]
x = strtoull(str, NULL, 10);
^~~~~~~~
strtoul
test.c:11:9: warning: ISO C90 does not support the ‘ll’ gnu_printf length modifier [-Wformat=]
printf("%llun", x);
^~~~~~~~
Эти предупреждающие сообщения — именно то, что я ожидал бы, учитывая документацию. Он уведомляет меня о том, что указанная мной функция не может быть найдена, и даже о том, что стандарт C90 не поддерживается unsigned long long
. Однако, когда я пытаюсь запустить этот код, он работает просто отлично, без сбоев или других типов ошибок. Он выводит значение 1234
по желанию. Итак, основываясь на этом эксперименте, у меня есть несколько вопросов, на которые, я надеялся, сможет ответить кто-то более опытный, чем я.
- Это связано с тем, что я не предоставляю необходимые флаги компиляции для обеспечения соблюдения «строгого» стандарта c98?
- Это тот случай, когда я неправильно понимаю документацию, или есть какая-то документация для самого gcc, на которую я должен ссылаться? И если да, то где я могу его найти?
- Есть ли что-то фундаментальное в процессе компиляции / компоновки, чего я не понимаю, что объясняет эту проблему?
- Почему я должен быть предупрежден о несовместимости, даже предупрежден о том, что вызываемая функция не существует, но код по-прежнему работает без проблем?
- Означает ли этот эксперимент, что
-std=c89 -pedantic
флаги фактически не обеспечивают соблюдение стандарта C89 / C90?
В качестве последнего замечания я не пытаюсь сказать, что хочу использовать эту функцию в C89, мне просто было любопытно узнать об ограничении совместимости, а затем запутался в ответе.
Заранее спасибо за любые ответы!
Комментарии:
1. Может быть, вы хотите
gcc ... -pedantic-errors ...
? 🙂2. То, что функция была добавлена в стандартную версию X, не означает, что компиляторы и (что более важно для этого) libc не могут предоставить ее и для версии X-1.
3. Попробуйте использовать число, которое не подходит,
int
и вы увидите, что оно работает не совсем нормально.4. @pmg Фантастика, это очень полезно, спасибо!
5. @Shawn Ah Я думаю, что понимаю, о чем вы говорите, поэтому, когда gcc охватывает весь стандарт c89, это не обязательно означает, что они предоставляют функции ТОЛЬКО для этого стандарта, спасибо! В случае, если я хотел бы очень строгого соблюдения определенного стандарта (не только c89, но и более поздних версий), есть ли простой способ сделать это?
Ответ №1:
С точки зрения компилятора C89 / C90, единственная ошибка в вашем коде — это использование unsigned long long
, которое выглядит как синтаксическая ошибка. Стандарт требует только, чтобы компилятор выдал «диагностику» в этом случае, и GCC сделал это с предупреждением «ISO C90 не поддерживает long long
«. Нет требования, чтобы эта ошибка была фатальной, и компилятор может решить обработать код каким-либо другим способом, если захочет. GCC, очевидно, предпочитает понимать его как long long
тип, который он поддерживает в качестве расширения для C89.
Использование strtoull
then просто выглядит как некоторая функция, которую вы создали, поскольку C89 не мог знать, что это имя будет особенным в какой-либо будущей версии стандарта. (Ну, они указали, что str
в будущем могут быть добавлены дополнительные функции, начинающиеся с <string.h>
, но это не делает ваш код незаконным для C89.) Вы не объявили его, но C89 разрешил неявные объявления, поэтому подразумевается, что он объявлен как int strtoull();
, т. Е. Возвращает int
и с неуказанными аргументами. AFAIK для неявных объявлений диагностика не требовалась, но GCC все равно решает ее выдать. Таким образом, он обрабатывается как любой другой вызов функции, не определенной в этом исходном файле, и компилятор предполагает, что какая-то другая часть вашей программы (включая библиотеки, которые вы используете) определит его.
И на самом деле это определяет какая-то другая часть вашей программы, а именно libc, поскольку ваш libc соответствует C99 и более поздним версиям. (Надеюсь, вы знаете, что libc не является частью GCC.) Авторы библиотек C обычно не предоставляют версию библиотеки, которая включает только функции из определенной стандартной версии, поскольку наличие такого количества разных библиотек было бы неудобным и неэффективным. Итак, связывание выполнено успешно.
Обратите внимание, однако, что из-за неявного объявления программа может работать некорректно. Компилятор сгенерирует код неправильно, предполагая, что strtoull
возвращает int
, что в зависимости от соглашений о вызовах вашей системы может вызвать всевозможные проблемы. На x86-64 это означает, что ваша программа будет просматривать только младшие 32 бита результата и расширит их до 64 бит. Поэтому, если вы попытаетесь преобразовать число, которое умещается в 32 бита, но не умещается long long
, вы получите неверный результат. Пример.
Если вам нужна программа, которая будет работать в системе, поддерживающей только C89 и ничего больше, вы несете ответственность за просмотр диагностики, выданной компилятором, и устранение соответствующих проблем. -pedantic-errors
Опция, упомянутая в комментариях, может помочь в этом, поскольку она приводит к сбою компиляции при выдаче такой диагностики.
Также было бы полезно, если бы вы могли найти libc только для C89, но это не проблема GCC. Но его неявные предупреждения о объявлении действительно помогают вам заметить, что вы вызвали функцию, которую вы, возможно, не предназначали для определения вашей программой.
В заключение, исторически частью философии дизайна GCC было то, что они не считают, что «обеспечение соблюдения стандарта» действительно является частью того, что они хотят делать. Они видели свою цель в написании компилятора, который помогает людям писать и компилировать полезные программы, а не компоновщика, который проверяет соответствие стандартам кодирования; они решили, что последний должен быть отдельным проектом, а не тем, который их интересует. Таким образом, они были либеральны в предоставлении расширений для стандартного языка и не особенно старательны в предоставлении программам способов избежать их использования. Они предоставили эту -pedantic
опцию, но, по-видимому, с некоторой неохотой, как вы можете судить по уничижительному названию.
Комментарии:
1. Хотя название
-pedantic
флага предполагает, что первоначальные авторы gcc не согласились с некоторыми решениями Комитета относительно ограничений, решение поддержать флаг, а не добиваться соответствия, предлагая флаг, который безоговорочно выдавал бы диагностический «Эта программа может нарушать ограничения» [что удовлетворяло бы требованиям стандартато, что попытка обработать программу, которая нарушает ограничение времени компиляции, выдает диагностику] указывает на то, что они стремились сделать свой компилятор полезным в качестве инструмента проверки соответствия. Далее, …2. … за последние 15 лет или около того разработчики gcc пришли к выводу, что в тех случаях, когда части стандарта и документации платформы определяют поведение некоторой конструкции, но другие части классифицируют конструкцию как UB, они рассматривают последнее как исключающее возможность того, что конструкция можетбыть непереносимым, но корректным на некоторых платформах, включая цель.
3. @supercat: Да, это все было до моего времени, но у меня такое чувство, что
-pedantic
название, вероятно, было выбрано Столлманом и отражало его взгляд на стандартизацию, и что изменение с тех пор было связано с его уменьшением участия в проекте. Возможно, разделение и объединение между gcc и egcs были еще одним маркером на этом пути.4. Мне искренне интересно узнать историю некоторых ранних версий gcc и философию «оптимизации», которая кажется полностью противоположной философии дизайна 1980-х gcc. В 1980-х годах, если конструкцию можно было легко интерпретировать каким-либо полезным образом, и не было веских причин не использовать ее с пользой, авторы gcc восприняли это как подсказку для полезной реализации конструкции. Сегодня, если нет мандата на полезную обработку конструкции, это интерпретируется как оправдание для того, чтобы считать ее «сломанной» и обрабатывать ее бессмысленно, даже если другие компиляторы обработали бы ее с пользой.