In old gcc
compilers, if you try to compile a program written with the c99 standard, a compilation warning is notified by the compiler. You must use the specific -std
option of the gcc
compiler, to explicitly specify the standard used in the program you want to compile:
gcc -Wall --pedantic -std=c99 file.c
For new gcc
compilers the c99 standard is the default. As a consequence, to know if a program is adherent with the old c90
standard, it must be compiled with the following options:
gcc -Wall --pedantic -std=c90 file.c
ISO/IEC 9899:1999 http://www.iso.org/iso/catalogue_detail.htm?csnumber=29237
Table which highlights supported features in GCC compilers https://gcc.gnu.org/c99status.html
In the following table the C standards are listed. The compiler predefines the following macros, that can be used to check if a given standard is supported and possibly, in the case the standard is not supported, to check if the compiler provides a C90 implementation.
Standard | NAME | Predefined macro |
---|---|---|
ANSI X3.159-1989 | C89 | __STDC__ |
ISO/IEC 9899:1990 | C90 | __STDC__ |
ISO/IEC 9899-1:1994 | C94 | __STDC_VERSION__ = 199409L |
ISO/IEC 9899:1999 | C99 | __STDC_VERSION__ = 199901L |
ISO/IEC 9899:2011 | C11 | __STDC_VERSION__ = 201112L |
Compiling with:
gcc -std=c99 c99_macro_std_version.c
the following program:
/* c99: example of macro useful to identify the standard used by the compiler */ #include <stdio.h> int main() { printf("__STDC__: %d __STDC_VERSION__: %ld\n", __STDC__, __STDC_VERSION__); return 0; }
the output is:
__STDC__: 1 __STDC_VERSION__: 199901
Function is inline inserted in the code of the program.
Usually it is better to avoid the use of inline
functions because:
inline
For more details: http://www.greenend.org.uk/rjk/tech/inline.html
Variables can be declared in any point of the source code. In the previous c90
version, the declaration of the variables was restricted to the start of a block.
This possibility is very useful for three main reasons:
for
loops can be initialized directly inside the loop with scope restricted to the loop:/* c99: example of a variable declared inside a for loop */ #include <stdio.h> int main() { int i = 123; for (int i=0; i<3; i++) printf("%d ", i);; printf("\nAFTER THE FOR: %d\n", i); return 0; }
The above code corresponds to the following code written in the c90
standard:
/* c90: how to restrict the scope of the variable used to manage the cycle in a for loop */ #include <stdio.h> int main() { int i = 123; { int i; for (i=0; i<3; i++) printf("%d ", i); } printf("\nAFTER THE FOR: %d\n", i); return 0; }
/* c99: limiting the scope of variables declaring them when needed or declaring them in a block inside a block */ #include <stdio.h> int main() { int x; x = 2; /* i cannot be used here */ int i; i = x; /* Block inside a block */ { /* This is another variable, it is not i=x */ int i; i = 3; /* At the end of the block i=3 no longer exist */ } /* i=x still exist */ return 0; }
Variable-length arrays (VLAs) are classical C arrays, but with a length not declared as a constant expression.
Drawbacks of VLAs are two:
c11
standard. Therefore, a program that makes use of VLAs could not be compiled by some “new” compilers.A first easy example is the following, where an array with unknown dimension is read from stdin
/* c99: VLAs, reading a vector with unknown dimension from stdin */ #include <stdio.h> int main() { int l; printf("Insert length: "); scanf("%d", &l); int v[l]; for(int i=0; i<l; i++) scanf("%d", &v[i]); for(int i=0; i<l-1; i++) printf("%d ", v[i]); printf("%d\n", v[l-1]); return 0; }
In this second example, VLAs are used in the function my_system
to concatenate two strings. The concatenation of the two strings is used as an argument of the system call system
.
/* c99: VLAs, system with the concatenation of two strings */ #include <stdio.h> #include <stdlib.h> #include <string.h> int my_system(char *s1, char *s2) { char s[strlen(s1) + 1 + strlen(s2) + 1]; strcpy(s, s1); strcat(s, " "); strcat(s, s2); return system(s); } int main() { my_system("ls", "-l"); return 0; }
Reference regarding malloc
vs. VLAs: http://www.drdobbs.com/the-new-cwhy-variable-length-arrays/184401444
Even if the long double
type was present from the very beginning in the first version of the C standard, the support in standard libraries of this type starts from the c99
version of the standard.
Constants of type long double
are identified by the L
or l
suffixes at the end of the number (e.g., 12.033134113425422423432L
).
The format specifier for printf
and scanf
functions family is “%Lf
” and “%LF
”, “%Lg
” and “%LG
” for a number with up to 6 digits of precision, and “%Le
” and “%LE
” for the hexadecimal format.
Usually in 32 bits architectures long double
are stored with an 80 bits precision, while in a 64 bit architecture they are usually stored with a precision of 128 bits (see https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format). The fact that dimension and characteristics of this type change depending on the architecture and on the compiler leads to compatibility problems when binary files containing long double
values are transferred between different PCs.
In the following program some statistics regarding the precision of long double
are inspected and an example regarding the printf
function is provided:
/* c99: some statistics regarding long double and example of the use of the printf function */ #include <stdio.h> #include <float.h> int main() { long double ld = 1342344.42423412442344141232412L; printf("LEN:\n"); printf(" Float len: %zu\n", sizeof(float)); printf(" Double len: %zu\n", sizeof(double)); printf("Long double len: %zu\n", sizeof(long double)); printf("\nNUMBER OF SIGNIFICANT DIGITS:\n"); printf(" Float significant digits = %d\n", FLT_DIG); printf(" Double significant digits = %d\n", DBL_DIG); printf("Long double significant digits = %d\n", LDBL_DIG); printf("\nPRINTF:\n"); printf("%Lf\n", ld); printf("%Lg\n", ld); printf("%Le\n", ld); return 0; }
Output:
LEN: Float len: 4 Double len: 8 Long double len: 16 NUMBER OF SIGNIFICANT DIGITS: Float significant digits = 6 Double significant digits = 15 Long double significant digits = 18 PRINTF: 1342344.424234 1.34234e+06 1.342344e+06
A good reference regarding float.h
library and floating points numbers in general: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/float.h.html
This new type and the relative unsigned
version has been added to the allowed standard C types.
In particular:
signed long long
integers. The standard guarantees that in a variable of such type can be stored a number at least in the range [−9223372036854775807, +9223372036854775807], and the minimum size of such a kind of data is 64 bits.ll
or LL
added at the end of the integer constant. Examples are 7ll, 7LL, 07LL, 0x7ll.printf
and scanf
functions the %lld
format specifier is typically used. Other format specifiers are possible, for example for octals or exadecimals numbers (e.g., %llo
, %llx
, %llX
).long long
(i.e., dimension of at least 64 bits), but only for unsigned positive integer numbers. As a consequence, the minimum range of number that can be stored in this type of variables is [0, +18446744073709551615].U
or u
concatenated with the suffixes ll
or LL
added at the end of the integer constant. Examples are 7Ull, 7LLu, 0x7llu.printf
and scanf
functions the %llu
format specifier is typically used. Other format specifiers are possible, for example for octals or exadecimals numbers (e.g., %llo
, %llx
, %llX
).
The <limits.h>
library defines the limits for long long int
type:
long long
in the range [LLONG_MIN,LLONG_MAX]unsigned long long
in the range [0,ULLONG_MAX]/* c99: the long long int type */ #include <stdio.h> #include <limits.h> int main() { long long int a; unsigned long long int b; printf("Range long long: [%lld, %lld]\n", LLONG_MIN, LLONG_MAX); printf("Range unisigned long long: [0, %llu]\n", ULLONG_MAX); a = -2; b = 2; printf("a: %lld b: %llu\n", a, b); printf("%3lld is hex %llX\n", a, a); printf("SIZE: %zu\n", sizeof(long long int)); return 0; }
Output:
Range long long: [-9223372036854775808, 9223372036854775807] Range unisigned long long: [0, 18446744073709551615] a: -2 b: 2 -2 is hex FFFFFFFFFFFFFFFE SIZE: 8
More details: http://www.drdobbs.com/the-new-c-integers-in-c99-part-1/184401323
The new _Bool
(or bool
defined as macro) type:
_Bool
a true
boolean value in C was a number other than 0, i.e., -15, 1, 123)short int
) occupies at least 2 bytes.
By including the header stdbool.h
the constants true
and false
are defined.
Casting: (bool)0.5
evaluates to true
, whereas (int)0.5
evaluates to 0
(false
).
The language guarantees that any two true values will compare equal (which was impossible to achieve before the introduction of the type)
/* c99: the _Bool type */ #include <stdio.h> #include <stdbool.h> /* Header for _Bool */ int main() { _Bool x; bool y; /* bool is a macro with the same meaning of _Bool */ x = true; y = true; if (x==y) printf("Equals\n"); printf("VALUES-> true: %d false: %d\n", true, false); printf("SIZES -> _Bool: %ld short: %ld\n", sizeof(_Bool), sizeof(short)); return 0; }
Output:
Equals VALUES-> true: 1 false: 0 SIZES -> _Bool: 1 short: 2
The c99
standard defines full support to complex numbers through the new complex
type defined in complex.h
. The three types of complex numbers that can be defined are listed in the following in increasing size order: float complex
, double complex
and long double complex
.
I
is a constant to identify an imaginary number. Functions creal()
and cimag()
have been defined to access the real or the imaginary part of a complex number, respectively. All math functions (e.g., sin()
, cos()
, sqrt()
) have an counterpart to be used with complex numbers. Such functions are the same defined in math.h
but their name starts with the suffix c
(e.g., csin()
, ccos()
, csqrt()
).
The following example should clarify the use of complex numbers:
/* c99: example regarding complex numbers */ #include <stdio.h> #include <complex.h> #include <tgmath.h> int main(void) { /* Square 1 */ double complex z1 = I * I; printf("I * I = %.1f+%.1fi\n", creal(z1), cimag(z1)); /* Square 2 */ double complex z2 = pow(I, 2); // imaginary unit squared printf("pow(I, 2) = %.1f+%.1fi\n", creal(z2), cimag(z2)); /* Conjugates numbers */ double complex z4 = 1+2*I, z5 = 1-2*I; // conjugates printf("(1+2i)*(1-2i) = %.1f+%.1fi\n", creal(z4*z5), cimag(z4*z5)); /* Square root */ double complex z6=csqrt(-1); printf("sqrt(-1) = %.1f+%.1fi\n", creal(z6), cimag(z6)); }
Output:
I * I = -1.0+0.0i pow(I, 2) = -1.0+0.0i (1+2i)*(1-2i) = 5.0+0.0i sqrt(-1) = 0.0+1.0i
The specific header tgmath.h
defines a type-generic macro for each mathematical function defined in both math.h
and complex.h
. Basically, for each math function there are 6 variants: 3 for types float
, double
and long double
, and 3 for types float complex
, double complex
and long double complex
.
A good reference can be downloaded here: http://en.cppreference.com/w/c/numeric/complex
Other characteristics added by the c99
standard and not discussed here are:
//
tgmath.h
with defines function for float
, double
, long double
, complex float
, complex double
and complex long double
(only some hints are provided in this guide)snprintf
(which is a safe version of the sprintf
function) and other new functionsc99
standard can be found in https://en.wikipedia.org/wiki/C99 or in the standard