C Standard Version Check - c

Platform: Ubuntu 14.04 LTS
Compiler: GCC 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
I am writing a header that needs to be backwards complaint with C90. I have some optional functions that are included if the complier supports C99 or C11. So I use the standard feature test macros. I just want to know if the way I do this is correct.
#if __STDC__
//code
#endif
#if __STDC_VERSION__ >= 199000L
//code
#endif
#if __STDC_VERSION__ >= 201100L
//code
#endif
I am assuming that if the compiler is strictly C90 or C89 then the compiler will error out and say __STDC_VERSION__ is undefined am I correct? If so should I use defined()? For example #if defined(__STDC__) rather than just #if __STDC__.

Undefined identifiers in preprocessor-conditionals are replaced by 0 if they are not argument to define, thus no error:
6.10.1 Conditional inclusion
3 Preprocessing directives of the forms
#if constant-expression new-line groupopt
#elif constant-expression new-line groupopt
check whether the controlling constant expression evaluates to nonzero.
4 Prior to evaluation, macro invocations in the list of preprocessing tokens that will become
the controlling constant expression are replaced (except for those macro names modified
by the defined unary operator), just as in normal text. If the token defined is
generated as a result of this replacement process or use of the defined unary operator
does not match one of the two specified forms prior to macro replacement, the behavior is
undefined. After all replacements due to macro expansion and the defined unary
operator have been performed, all remaining identifiers (including those lexically
identical to keywords) are replaced with the pp-number 0, and then each preprocessing
token is converted into a token. [...]
You can use the #error directive to force a compilation-error.

Related

Double nested parentheses throw an error inside macro when using the defined operator

I am writing an emulator and I want it to be exactly like macros in C. The following code confuses me:
This is valid code and returns true:
#if ((((1 + ((2)))))) == 3
This is also valid code and returns true:
#define hi hello
#if defined (hi)
But this will throw an error and not compile:
#define hi hello
#if defined ((hi))
Is there a special reason why this does not work?
It seems like it should work.
Here is a link if you want to test macros online: https://godbolt.org/ (put -E as argument)
Here a link that explains the defined operator: https://gcc.gnu.org/onlinedocs/gcc-8.4.0/cpp/Defined.html
#if does a mathematical evaluation. Your parenthesis get evaluated as in a C expression. The more the merrier, they don't change the value the expression evaluates to (except when they do, of course, when set to change operator precedence).
#ifdef identifier / #if defined identifier / #if defined( identifier ) (the parenthesis are optional for the defined operator, and not allowed for #ifdef) checks if the given identifier is defined. An identifier begins with a letter or an underscore, and contains only letters, underscores, or digits.
hi is an identifier.
(hi) is not.
C 2018 6.10.1 specifies the behavior of define in a #if or #elif directive. It says the expression that controls conditional inclusion:
… may contain unary operator expressions of the form
defined identifier
or
defined ( identifier )
which evaluate to 1 if the identifier is currently defined as a macro
name…
Thus, the operand of defined is not a general C expression but is only “identifier” or “( identifier )”.

Macro name must be an identifier

please, tell me what's wrong with this code:
// part of main.c
#if have(some_macro)
printf("Import succeeded!\n");
#else
# error Import failed!
#endif
// part of utils.h
#define have(macro) defined(macro)
If I write just #if defined(some_macro) it works.
What's the problem?
You cannot have anything following #if expand to defined. That is subject to undefined behavior.
From the C99 Standard (emphasis mine):
6.10.1 Conditional inclusion
...
3 Preprocessing directives of the forms
# if *constant-expression new-line group<sub>opt</sub>*
# elif *constant-expression new-line group<sub>opt</sub>*
check whether the controlling constant expression evaluates to nonzero.
4 Prior to evaluation, macro invocations in the list of preprocessing tokens that will become the controlling constant expression are replaced (except for those macro names modified by the defined unary operator), just as in normal text. If the token defined is generated as a result of this replacement process or use of the defined unary operator does not match one of the two specified forms prior to macro replacement, the behavior is undefined.

Effect of single hash in object-like macro

Is # permitted in an object-like macro, and if so, what happens?
The C standard only defines the behaviour of # in a macro for function-like macros.
Sample code:
#include <stdio.h>
#define A X#Y
#define B(X) #X
#define C(X) B(X)
int main()
{
printf(C(A) "\n");
}
gcc outputs X#Y, suggesting that it permits # to be present and performs no special processing. However, since the definition of the # operator does not define the behaviour in this case, is it actually undefined behaviour?
As you noticed, # only has a defined effect in function-like macros. § 6.10.3.2/1 (all references to the standard are to the C11 draft (N1570)). To see what happens in object-like macros, we must look elsewhere.
A preprocessing directive of the form
# define identifier replacement-list new-line
defines an
object-like macro
that causes each subsequent instance of the macro name
to be replaced by the replacement list of preprocessing tokens that constitute the
remainder of the directive. [...]
§ 6.10.3/9
Therefore, the only question is whether # is allowed in a replacement-list. If so, it takes part in replacement as usual.
We find the syntax in § 6.10/1:
replacement-list:
pp-tokens (opt.)
pp-tokens:
preprocessing-token
pp-tokens preprocessing-token
Now, is # a valid preprocessing-token? § 6.4/1 says:
preprocessing-token:
header-name
identifier
pp-number
character-constant
string-literal
punctuator
each non-white-space character that cannot be one of the above
It's certainly not a header-name (§ 6.4.7/1), it's not allowed in identifier tokens (§ 6.4.2.1/1), nor is it a pp-number (which is basically any number in an allowed format, § 6.4.8/1), nor a character-constant (such as u'c', § 6.4.4.4/1) or a string-literal (exactly what you'd expect, e.g. L"String", § 6.4.5/1).
However, it is listed as a punctuator in § 6.4.6/1. Therefore, it is allowed in the replacement-list of an object-like macro and will be copied verbatim. It is now subject to rescanning as described in § 6.10.3.4. Let us look at your example:
C(A) will be replaced with C(X#Y). # has no special effect here, because it is not in the replacement-list of C, but its argument. C(X#Y) is obviously turned into B(X#Y). Then B's argument is turned into a string literal via the # operator in B's replacement-list, yielding "X#Y"
Therefore, you don't have undefined behavior.

Is it correct to say if any non-macro variable that is used in a conditional directive evaluates to 0?

Let's say I have the following piece of code. Is it correct to say that the conditional directive always evaluates to zero since M is expanded before compile time where the value of i is zero according to C specification? Could someone explain it if I'm wrong:
#include <stdio.h>
#define M i
int main() {
int i = 10;
#if M == 0
i = 0;
#endif
printf("%d", i);
return 0;
}
With #if identifiers that are not macros are all considered to be the number zero. The gcc docs for #if lay out the requirements nicel, which says (emphasis mine going forward):
#if expression
expression is a C expression of integer type, subject to stringent restrictions. It may contain
Integer constants.
Character constants, which are interpreted as they would be in normal code.
Arithmetic operators for addition, subtraction, multiplication, division, bitwise operations, shifts, comparisons, and logical operations (&& and ||). The latter two obey the usual short-circuiting rules of standard C.
Macros. All macros in the expression are expanded before actual computation of the expression's value begins.
Uses of the defined operator, which lets you check whether macros are defined in the middle of an ‘#if’.
Identifiers that are not macros, which are all considered to be the number zero. This allows you to write #if MACRO instead of #ifdef MACRO, if you know that MACRO, when defined, will always have a nonzero value. Function-like macros used without their function call parentheses are also treated as zero.
and notes that:
In some contexts this shortcut is undesirable. The -Wundef option causes GCC to warn whenever it encounters an identifier which is not a macro in an ‘#if’.
This is covered in the draft C99 standard section 6.10.1 Conditional inclusion paragraph 4 which says:
[...]After all replacements due to macro expansion and the defined unary
operator have been performed, all remaining identifiers (including those lexically
identical to keywords) are replaced with the pp-number 0, and then each preprocessing
token is converted into a token.[...]
Yes, they are treated as the number 0.
C11 §6.10.1 Conditional inclusion
... After all replacements due to macro expansion and the defined unary operator have been performed, all remaining identifiers (including those lexically identical to keywords) are replaced with the pp-number 0, and then each preprocessing token is converted into a token.
pp-number here refers to preprocessing numbers in §6.4.8.

The subtle difference between #ifdef and #if for a macro defined as 0

Given the C file below:
$ cat macros.c
#ifdef MACRO
# error MACRO is defined
#else
# error MACRO is undefined
#endif
#if MACRO
# error MACRO is non-zero
#else
# error MACRO is zero
#endif
What is the expected output of the following?
$ gcc -c macros.c
$ gcc -DMACRO -c macros.c
$ gcc -DMACRO=0 -c macros.c
Answer: Here's what the gcc's preprocessor on my machine does.
$ gcc -c macros.c
macros.c:4:4: error: #error MACRO is undefined
macros.c:9:4: error: #error MACRO is zero
$ gcc -DMACRO -c macros.c
macros.c:2:4: error: #error MACRO is defined
macros.c:7:4: error: #error MACRO is non-zero
$ gcc -DMACRO=0 -c macros.c
macros.c:2:4: error: #error MACRO is defined
macros.c:9:4: error: #error MACRO is zero
$
Lesson: #ifdef MACRO evaluates to true for defined-ness even if the defined value is 0 (zero).
Another C preprocessor gotcha! Is this how it should be as per the C standard?
Any undefined macro is treated as it were defined as 0 for the purposes of evaluating the controlling expressions of #if statements. From C99 §6.10.1/3-4 (emphasis added):
3) Preprocessing directives of the forms
# if constant-expression new-line groupopt
# elif constant-expression new-line groupopt
check whether the controlling constant expression evaluates to nonzero.
4) Prior to evaluation, macro invocations in the list of preprocessing tokens that will become
the controlling constant expression are replaced (except for those macro names modified
by the defined unary operator), just as in normal text. If the token defined is
generated as a result of this replacement process or use of the defined unary operator
does not match one of the two specified forms prior to macro replacement, the behavior is
undefined. After all replacements due to macro expansion and the defined unary
operator have been performed, all remaining identifiers (including those lexically
identical to keywords) are replaced with the pp-number 0, and then each preprocessing
token is converted into a token. [...]
So, for example, expressions like this:
#if !FOO
Will evaluate to 1 if FOO is undefined, since it will be treated as a 0, and then !FOO would be evaluated as !0, which is 1.
#ifdef only cares if MACRO has been defined or not. Value doesn't matter.
#if checks for the value of MACRO and evaluates MACRO accordingly.
That is correct behaviour
The behavior is as per the standard.
C99 Standard: 6.10.1 Conditional inclusion:
Para 2: Preprocessing directives of the forms
# if constant-expression new-line groupopt
# elif constant-expression new-line groupopt
check whether the controlling constant expression evaluates to nonzero.
Para 4: Preprocessing directives of the forms
# ifdef identifier new-line groupopt
# ifndef identifier new-line groupopt
check whether the identifier is or is not currently defined as a macro name. Their
conditions are equivalent to #if defined identifier and #if !defined identifier
respectively.

Resources