80

C99 has been around for over 10 years, but support for it has been slow coming, so most developers have stuck with C89. Even today, I'm sometimes mildly surprised when I come across C99 features in C code.

Now that most major compilers support C99 (MSVC being a notable exception, and some embedded compilers also lagging behind), I feel that developers who work with C probably ought to know about what C99 features are available to them. Some of the features are just common features that were never standardized before (snprintf, for instance), or are familiar from C++ (flexible variable declaration placement, or single-line // comments), but some of the new features were first introduced in C99 and are unfamiliar to many programmers.

What do you find the most useful new features in C99?

For reference, the C99 standard (labelled as a draft, but identical to the updated standard, as far as I know), the list of new features, and the GCC C99 implementation status.

One feature per answer, please; feel free to leave multiple answers. Short code examples demonstrating new features are encouraged.

Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
  • 2
    There should be a similar wiki for features that people *hate* in C99! – Alok Singhal Jan 12 '10 at 06:25
  • Well, there was a question about harmful or unsupported C99 features http://stackoverflow.com/questions/1898890/what-c99-features-are-considered-harmful-or-unsupported – Brian Campbell Jan 12 '10 at 06:28
  • Thanks. You should change the link text to indicate that it's a draft, not the actual standard, and link to n1256 while you're at it :-). BTW, looking at http://gcc.gnu.org/c99status.html, I wouldn't say most of C99 is supported by gcc. And since gcc is one of the most widely used C compilers, ... – Alok Singhal Jan 12 '10 at 06:33
  • 2
    Note that in the embedded processor arena, C99 may still not be well supported. – Craig McQueen Jan 12 '10 at 06:50
  • 1
    @Alok I would call that level of support most of the features; I suppose it depends on how you define it, but I think most of the significant features that people want to use are supported, leaving aside a few library issues. @Craig Fair enough, added a disclaimer about embedded compilers. – Brian Campbell Jan 12 '10 at 07:06
  • Of course, the biggest problem with C99 is that the MSVC compilers do not support many (any?) of the most useful features listed below. This hobbles people who have to write code that works on both Unix and Windows. – Jonathan Leffler Jan 12 '10 at 18:18
  • Of course; hence the "but one" in my original question. Now, you don't have to compile with MSVC on Windows; you can use MinGW, or the Intel C Compiler, or any of a number of other compilers that do support C99. – Brian Campbell Jan 12 '10 at 18:32
  • Wow - it's amazing, being only a once-in-a-while C programmer, how much I have actually used these features from a standard that has yet to be fully implemented. +1 for the edifycation! – new123456 Jul 13 '11 at 03:39

17 Answers17

80

I'm so used to typing

for (int i = 0; i < n; ++i) { ... }

in C++ that it's a pain to use a non-C99 compiler where I am forced to say

int i;
for (i = 0; i < n; ++i ) { ... }
Jon Reid
  • 20,545
  • 2
  • 64
  • 95
73

stdint.h, which defines int8_t, uint8_t, etc. No more having to make non-portable assumptions about how wide your integers are.

uint32_t truth = 0xDECAFBAD;
Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
67

I think that the new initializer mechanisms are extremely important.

struct { int x, y; } a[10] = { [3] = { .y = 12, .x = 1 } };

OK - not a compelling example, but the notation is accurate. You can initialize specific elements of an array, and specific members of a structure.

Maybe a better example would be this - though I'd admit it isn't hugely compelling:

enum { Iron = 26, Aluminium = 13, Beryllium = 4, ... };

const char *element_names[] =
{
    [Iron]      = "Iron",
    [Aluminium] = "Aluminium",
    [Beryllium] = "Beryllium",
    ...
};
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 6
    That is a compelling demonstration. There is a huge amount of enum-tables with corresponding string-tables out there. – u0b34a0f6ae Nov 04 '11 at 09:18
  • 5
    @ColeJohnson: If you look carefully at the second example, you will observe that the initializers are listed out of order — but will work correctly. That can't be done simply with defines. The first example initializes only index 4 of the array; that can't be done simply with defines, either. – Jonathan Leffler Feb 25 '13 at 15:26
  • 2
    It should also be noted that using the same index more than once will override the first usage with the second - see my question here, for example: http://stackoverflow.com/questions/16742467/c99-designated-initializer-duplicate-index-not-flagged-at-all-in-build-output-or – johnny May 24 '13 at 19:49
53

Support for one-line comments beginning with //.

Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
gameover
  • 11,813
  • 16
  • 59
  • 70
51

Variable length arrays:

int x;
scanf("%d", &x);
int a[x];
for (int i = 0; i < x; ++i)
    a[i] = i * i;
for (int i = 0; i < x; ++i)
    printf("%d\n", a[i]);
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • 3
    You really think VLA arrays are that great? C11 makes them optional. – Z boson Apr 28 '15 at 08:13
  • 3
    Just don't forget to apply input sanitation to prevent a stack overflow (or stack corruption, if x is negative): `if (x < 0) x = 0; else if (x > 1024) x = 1024;` – Andrew D'Addesio Jan 03 '16 at 04:13
42

Being able to declare variables at locations other than the start of a block.

mikecsh
  • 852
  • 7
  • 12
  • 2
    I'm not so keen on this. To my mind, variables should not only be brought into scope when needed, but removed from scope when not needed. Variables mid-block almost invariably hang around in scope longer than they should. – supercat Mar 15 '11 at 15:28
  • 5
    @supercat you'd prefer `int a; int b; a = f(); b = g();` to `int a = f(); int b = g();` ? declaring a variable close to where it gets initialized is huge for reducing errors. – Ryan Haining Jun 17 '14 at 21:26
  • @RyanHaining: Alternatively, create a nested scope block (I just wish there was a standard syntax which said "this block is just for scoping"; one can use easily use a null macro for that purpose, but that seems a bit ugly. – supercat Jun 17 '14 at 22:06
  • @supercat There is a standard syntax for that: `/* this block is just for scoping */ {` `} /* scope */` :) – alx - recommends codidact May 04 '19 at 14:52
  • @CacahueteFrito: I think `if(1) /* Scoping block */ { ` `}` is a bit better, but a bigger issue is that scoping and control are orthogonal concepts. While there are times when it makes sense to scope things within a loop, it's more often useful to have things scoped just around the loop, and there are also many times when code will create objects to holding values that will be needed in the creation of longer-lived objects, but never after that (e.g. `float dx=x2-x1, dy=y2-y1; float distance = sqrt(dx*dx+dy*dy);`. There's no reason dx and dy should need to be kept in scope. – supercat May 04 '19 at 17:19
  • Extremely valuable feature. I do not share @supercat reservations. By defining and initialising the variable where it is needed (in association with `for(` declarations) I have managed to reduce significantly the complexity of the code. In 2010 I would have not known, now after converting several thousands of line of code, I was surprised at the positive effect it has on the code quality. Several wrong initialisation bugs have also been found this way. – Patrick Schlüter Jun 13 '22 at 06:12
  • wrong initialisation bugs = variable defined at fn level and initialised with 0 or NULL, then the first usage is done somewhere down in the function but its init value should have been something else. I was extremely surprised how often this bug was present in our code base. – Patrick Schlüter Jun 13 '22 at 06:19
  • @PatrickSchlüter: Would it not be useful, in something like my example, to be able to have dx and dy leave scope while keeping distance within? – supercat Jun 13 '22 at 15:03
  • @supercat yes it would but my experience is that most of the time, it does. As very often the variable you move from the function scope, is a variable that can be defined inside an existing if or loop block (`for(int i;` being very often encountered). The feature is a net positive even if it is 100% theoretically not perfect. – Patrick Schlüter Jun 16 '22 at 06:14
36

Variadic macros. Makes it easier to generate boilerplate code with unlimited number of arguments.

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
36

snprintf() - seriously, it's worth a lot to be able to do safe formatted strings.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
30

Flexible array members.

6.7.2.1 Structure and union specifiers

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. With two exceptions, the flexible array member is ignored. First, the size of the structure shall be equal to the offset of the last element of an otherwise identical structure that replaces the flexible array member with an array of unspecified length) Second, when a . (or ->) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. If this array would have no elements, it behaves as if it had one element but the behavior is undefined if any attempt is made to access that element or to generate a pointer one past it.

Example:

typedef struct {
  int len;
  char buf[];
} buffer;

int bufsize = 100;
buffer *b = malloc(sizeof(buffer) + sizeof(int[bufsize]));
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Gregory Pakosz
  • 69,011
  • 20
  • 139
  • 164
  • 2
    +1 for finally making this kosher. It's in every TCP/IP socket code I've ever seen. – slebetman Sep 18 '10 at 22:55
  • Indeed, I would consider it far more kosher than the common trick of using buf[1] and subtracting 1 from the malloc size. The trick may be common, but I would regard it as Undefined Behavior (proper would be using buf[MAX_SIZE] and subtracting MAX_SIZE from the malloc size) since a compiler's generated indexing code may depend upon the perceived size of buf[]. – supercat Mar 15 '11 at 15:26
29

Compound literals. Setting structures member-by-member is so '89 ;)

You can also use them to get pointers to objects with automatic storage duration without declaring unnecessary variables, eg

foo(&(int){ 4 });

insteand of

int tmp = 4;
foo(&tmp);
Christoph
  • 164,997
  • 36
  • 182
  • 240
25

The bool type.

You can now do something like that:

bool v = 5;

printf("v=%u\n", v);

will print

v=1
Patrick Schlüter
  • 11,394
  • 1
  • 43
  • 48
20

Support for inline functions.

CodeRain
  • 6,334
  • 4
  • 28
  • 33
  • In practice, GCC usually ignores the inline keyword when deciding which functions to inline, and automatically inlines things based on its own heuristics unless you force it to do otherwise. – daf Jan 12 '10 at 15:53
  • 8
    daf: `inline` still helps though, because it allows you to define the function in more than one translation unit - so you can put it in a header file, which gives cross-module inlining opportunities. – caf Jan 13 '10 at 01:07
  • @daf `inline __attribute__((force_inline))` – Cole Tobin Feb 25 '13 at 23:34
20

Compound literals, already mentioned but here's my compelling example:

struct A *a = malloc(sizeof(*a));
*a = (struct A){0};  /* full zero-initialization   */
/* or */
*a = (struct A){.bufsiz=1024, .fd=2};   /* rest are zero-initialized.  */

It's a clear way to initialize data even if it's on the heap. There is no way to forget to zero-initialize something.

u0b34a0f6ae
  • 48,117
  • 14
  • 92
  • 101
17

The restrict keyword. Especially when you crunch numbers...

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
15

Unicode escape sequence support:

printf("It's all \u03B5\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC to me.\n");

Or even, literal Unicode characters:

printf("日本語\n");

(note: may not work depending on your locale; portable support for different encodings will take more work than this)

Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
13

Hexadecimal floating point constants (0x1.8p0f) and conversion specifiers (%a, %A). If you deal with low-level numerical details frequently, these are an enormous improvement over decimal literals and conversions.

They save you from worries about rounding when specifying constants for an algorithm, and are immensely useful for debugging low-level floating-point code.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
  • 3
    Yep, I just used these the other day when trying to explain to someone how floating point numbers work in another Stack Overflow question. – Brian Campbell Jan 12 '10 at 19:41
9

Personally, I like the acknowledgment of IEC 60559:1989 (Binary floating-point arithmetic for microprocessor systems) and much better floating-point support.

In a similar vein, setting and querying the floating-point rounding mode, checking for Nan/Infinity/subnormal numbers, etc., is great to have.

Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
  • Well, MS doesn't like to do this – Cole Tobin Nov 14 '12 at 23:06
  • C isn't really well designed for the concept of rounding modes, since they mean that floating-point math needs to be treated as having side-effects. Having something better than "hope-for-the-best" floating-point semantics is useful, but I don't know how many implementations really honor all the requirements of Annex F. – supercat Jan 22 '17 at 00:30