Дата и время публикации: Дата и время модификации:
## 1. Назначение ### 1.1 Редакцию стандарта ISO С В компиляторе gcc версия стандарта ISO С определяется макросами ```__STDC__``` и ```__STDC_VERSION__``` : Макрос ```__STDC__``` [расширяется](https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html) до единичной константы, чтобы показать, что данный компилятор соответствует стандарту ```ISO C``` (_ISO Standard C_). Макрос ```__STDC_VERSION__``` расширяется до числовой версии стандарта С, являясь константным значением длинного целого в форме ```yyyymmL```, где ```yyyy``` и ```mm``` содержат значения года и месяца выхода стандарта. Мaкрос ```__STRICT_ANSI__``` появиться, если были применены строгие требования к соблюдению стандарта через опцию -std или -ansi . Так, значение макроса означает [использование](https://github.com/cpredef/predef/blob/master/Standards.md) редакции стандарта С и задается [опцией -std](https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html) : | Name| Macro | Standard | Option | |------|---------------------------------|---------------------|--------------------| | C89 | ```__STDC__``` | ANSI X3.159-1989 | -std=c89, -ansi | | C90 | ```__STDC__``` | ISO/IEC 9899:1990 | -std=c90, iso9899:1990 | | C94 | ```__STDC_VERSION__ = 199409L```| ISO/IEC 9899-1:1994 | iso9899:199409 | | C99 | ```__STDC_VERSION__ = 199901L```| ISO/IEC 9899:1999 | c99, iso9899:1999| | C11 | ```__STDC_VERSION__ = 201112L```| ISO/IEC 9899:2011 | c1x, iso9899:2024| | C17 | ```__STDC_VERSION__ = 201710L```| ISO/IEC 9899:2018 | c17, -pedantic | | C23 | ```__STDC_VERSION__ = 202311L```| ISO/IEC 9899:2024 | c23 | Значения опции компилятора -std, поддерживающие GNU диалект и соответствующие стандарту : * ```gnu90, gnu89``` ─ стандарту ```ISO C90```, * ```gnu99, gnu9x``` ─ стандарту ```ISO C99```, * ```gnu11, gnu1x``` ─ стандарту ```ISO C11```, * ```gnu17, gnu18``` ─ стандарту ```ISO C17```, * ```gnu23, gnu2x``` ─ стандарту ```ISO C23``` . Соответственно, если будет задана одно из только что выше перечисленых значений опции -std, ```__STRICT_ANSI__``` не будет определена. Начиная с версии ```GCC 14.2```, [использовать](https://gcc.gnu.org/releases.html) ```gnu2x``` запрещено. Для того, чтобы быстро [определить](https://stackoverflow.com/a/68717818/24648605) какие редакции стандарта С поддерживает компилятор, нужно в командной строке задать:
$ gcc --version && gcc -dM -E - < /dev/null| grep "__STDC_"
gcc (Debian 10.2.1-6) 10.2.1 20210110
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#define __STDC_HOSTED__ 1
#define __STDC_UTF_16__ 1
#define __STDC_IEC_559__ 1
#define __STDC_ISO_10646__ 201706L
#define __STDC_IEC_559_COMPLEX__ 1
#define __STDC_VERSION__ 201710L
#define __STDC_UTF_32__ 1
#define __STDC__ 1
Как следует из вывода, компилятор gcc поддерживает C89, так же называемый ANSI C, или ISO C90, потому что макрос ```__STDC__``` расширен до 1 . В тоже время, макросу __STDC_VERSION__ установлено значение ```201710L```, что соответствует стандарту ```ISO/IEC 9899:2018``` . При этом следует помнить, что когда ```-std=c89```, ```-ansi``` и ```iso9899:1990``` макрос ```__STDC_VERSION__``` не будет определен, но появиться макрос ```__STRICT_ANSI__``` :
$ gcc --version && gcc -std=c89 -dM -E - < /dev/null| grep "__STDC_\|STRICT_ANSI"
gcc (Debian 10.2.1-6) 10.2.1 20210110
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#define __STDC_HOSTED__ 1
#define __STDC_IEC_559__ 1
#define __STDC_ISO_10646__ 201706L
#define __STDC_IEC_559_COMPLEX__ 1
#define __STRICT_ANSI__ 1
#define __STDC__ 1
### 1.2 Откланение от редакции стандарта ```ISO С```
В компиляторе GCC реализованы [свои собственные расширения](https://rjaan.narod.ru/docs/dev/gnucs/gcc-extensions.html), которые отсутствуют в стандарте ISO языка С.
Так, если указать опцию -pedantic или -Wpedantic, на выходе GCC закидает предупреждающими сообщениями, если встретит что-то в обход стандарта. Поэтому, когда используются такого рода опции или -std с перечисленными выше значениями, нужно учитывать, что какое-либо отклонение от стандарта может сопровождаться не только выдачей ни к чему обязывающих сообщений, но и заставят компилятор завершить работу с ошибкой.
Поэтому рекомендуется использовать опцию ```-pedantic```, чтобы находить в коде отклонения от стандартов ```ISO C``` в виде расширений GCC через формирование предупреждений о их наличии. А использование вместе опции ```-std=c89 -pedantic``` [позволят](https://stackoverflow.com/a/2855148/24648605) легко портировать код между различными компиляторами на их платформах.
## 2. Использование
### 2.1 Определение редакции стандарта ISO C
Из всех вариантов определения и предопределения редакции ISO C мне приглянулся проект [cpredef/predef](https://github.com/cpredef/predef/blob/master/Standards.md), который предлагает предопределять по макросам ```__STDC__``` и ```__STDC_VERSION__``` макрос ```PREDEF_STANDARD_C_yyyy``` через макро if-выражение, определяя ```yyyy``` согласно значения в макрос ```__STDC_VERSION__``` с некоторыми правками для компилятора GCC :
...
/* abi_common.h */
...
#if defined(__STDC__) && defined(__STRICT_ANSI__)
# define PREDEF_STANDARD_C_ANSI 0x0c89
# if defined(__STDC_VERSION__)
# if (__STDC_VERSION__ >= 199409L)
# define PREDEF_STANDARD_C_1994 0x0c94
# endif
# if (__STDC_VERSION__ >= 199901L) 0x0c99
# define PREDEF_STANDARD_C_1999
# endif
# if (__STDC_VERSION__ >= 201112L)
# define PREDEF_STANDARD_C_2011 0x0c99
# endif
# if (__STDC_VERSION__ >= 201710L) 0x0c18
# define PREDEF_STANDARD_C_2018
# endif
# if (__STDC_VERSION__ >= 202311L)
# define PREDEF_STANDARD_C_2023 0x0c23
# endif
# endif
# elif defined(__STDC__) && !defined(__STRICT_ANSI__)
# define PREDEF_STANDARD_GNUC 0xfc00
#endif
...
Макрос PREDEF_STANDARD_GNUC будет определен, если ```__STRICT_ANSI__``` не будет в свою очередь установлен черз опцию ```-std``` или ```-ansi``` .
Соответственно, чтобы избежать ошибок, когда компилятору будет указана опция ```-std=c89``` или ```-ansi``` проект [cpredef/predef](https://github.com/cpredef/predef/blob/master/Standards.md) рекомендует использовать макрос PREDEF_STANDARD_C_yyyy, например для "удаления" ключевых слов, таких как :
#if ( PREDEF_STANDARD_C == PREDEF_STANDARD_C_ANSI)
# define const
# define volatile
#endif
В некоторых случаях, например, inline-функций :
...
#if (PREDEF_STANDARD_C >= PREDEF_STANDARD_C_1999)
inline int foo(int a, int b) { return ( 2 * (b+a) ) ; }
#else
# define foo(a,b) (2 * (b+a))
#endif /* PREDEF_STANDARD_C_1999 */
...
int a = 5,
b = 3,
c;
c = foo(a,b); /* c=16 */
Необходимо проверять, что предустановленный стандарт компилятору будет отвечать как минимуму ISO/IEC 9899:1999 , в котором появилась поддержка inline-функций. В противном случае, следует использовать макроопределение, содержащее арифметическое выражение ```2 * (b+a)``` .
### 2.2 Определение отклонений от стандарта GNU C
Наличие опции ```-pedantic``` позволяет найти отклонения от стандарта GNU C, например, использование расширений компилятора GCC :
vim float_to_binconv.c
...
19 #define MSB_FIND(n)({\
20 unsigned int __n = (unsigned int)n,\
21 __idx = 0, __ctx = -1; \
...
28 1 << __idx ;})
...
Попытка компиляции файла ```float_to_binconv.c``` с опцией ```-pedantic``` приведет к выводу сообщения ```-Wpedantic```, что запрещено использовать операторы-выражения в ISO C :
gcc -pedantic float_to_binconv.c -o float_to_binconv
float_to_binconv.c: In function ‘main’:
float_to_binconv.c:19:20: warning: ISO C forbids braced-groups within expressions [-Wpedantic]
19 | #define MSB_FIND(n)({\
| ^
...