Skip to content
Nicholas Clark edited this page May 19, 2021 · 9 revisions

C99

This page exists for "requirements gathering". Once we're done, this page will be replaced by finished documentation in blead.

For various reasons C99 took a long time to become widely available. We still can't rely on all of it (certainly not the parts C11 made optional) but there are several features we'd like to use, if we know they work everywhere.

To clearly document what we can use, we need to figure this out - in particular, the trade off between "supporting awkward compilers" and "useful".

Hence we need to figure out what we can use, so we can document this.

We already support/fake

  • bool
  • static inline
  • static assert

(And even adding support for bool more than a decade after C99, we hit compiler bugs for things like converting a pointer to a bool, and bool in ternaries. This stuff hasn't been battle tested widely.)

We already exceed C89 minimally required limits on

  • line length after macro expansion
  • cases in a switch statement

without problems, so we shouldn't worry about staying strictly within a C89 straight jacket for these.

For what we might need gcc 3.1 or later is fine. 19 year old gcc will be fine...

Whilst not the default,

  • xlc on AIX officially got c99 support in July 2002 with version V6.0 -- -qlanglvl=stdc99
  • HP aC/C++ references C99 support in documents from 2004 -- -AC99
  • VMS vendor compiler?
  • MSVC since when?

Open questions are:

// comments

testcase

#include <stdio.h>

int main(int argc, char **argv) {
    // Comment
    puts("Built "
         // Comment
         "// comments");
    return 0;
}

benefits

None have been clearly explained.

use blocked by

  • vendor compiler on VMS?
  • AIX xlc
  • HP-UX cc (at least with A.06.28.02)
  • first MSVC that supports this?

flexible array members

testcase

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct greeting {
    unsigned int len;
    char message[];
};

int main(int argc, char **argv) {
    const unsigned int len = strlen(argv[0]);
    struct greeting *g = malloc(sizeof(struct greeting) + len + 1);

    g->len = len;

    memcpy(g->message, argv[0], len);
    printf("Built flexible arrays (as %*s)\n", g->len, g->message);

    return 0;
}

benefits

This is standards conformant, unlike the "unwarranted chumminess with the compiler" hack we currently use of

struct greeting {
    unsigned int len;
    char message[1];
};

However, we use that hack already, and it works. So what do we gain?

Also, (IIRC) flexible array members are not allowed within nested structs or unions, whereas the hack we have works as the last member within nested aggregates.

use blocked by

  • vendor compiler on VMS
  • AIX xlc
  • HP-UX cc (at least with A.06.28.02)
  • first MSVC that supports this?

64 bit integer types

even if slow, and just for arithmetic and bitwise operators

testcase

#include <stdio.h>

int main(int argc, char **argv) {
    unsigned long long hex = 18364758544493064720ULL;
    unsigned long long big = 0x9e3779b97f4a7c15;
    unsigned long small = 0x9e3779b7;
    puts("Built 64 bit integer types");
    printf("%llX >> 4 is %llX\n", hex, hex >> 4);
    printf("%llX * %lX is %llX\n", big, small, big * small);
    return 0;
}

benefits

  • Certain calculations can be expressed directly

use blocked by

  • vendor compiler on VMS?
  • AIX xlc
  • HP-UX cc (at least with A.06.28.02)
  • first MSVC that supports this?

variadic macros

testcase

#include <stdio.h>
#include <stdarg.h>

void greet(char *file, unsigned int line, char *format, ...) {
    va_list ap;

    va_start(ap, format);
    printf("I was called at %s %u to say: ", file, line);
    vprintf(format, ap);
    va_end(ap);
}

#define logged_greet(...) greet(__FILE__, __LINE__, __VA_ARGS__)

int main(int argc, char **argv) {
    char *action = "Built";
    char *source = "variadic macros";
    logged_greet("%s %s\n", action, source);
    logged_greet("argv[0] is %s\n", argv[0]);
    return 0;
}

benefits

  • very useful for making sprintf style defines actually DWIM with PERL_NO_CONTEXT

use blocked by

  • vendor compiler on VMS?
  • AIX xlc
  • HP-UX cc (at least with A.06.28.02)
  • first MSVC that supports this?

mixed declarations and code

testcase

#include <stdio.h>

int main(int argc, char **argv) {
    char *action;
    action = "Built";
    const char *source = "mixed declarations and code";
    printf("%s %s\n", action, source);
    return 0;
}

benefits

  • Can directly reduce line count without reducing readability
  • Can indirectly make it easier to use const
  • We have some macros which introduce declarations - knowing the ordering conventions is a barrier to entry

use blocked by

  • vendor compiler on VMS
  • AIX xlc
  • HP-UX cc (at least with A.06.28.02)
  • first MSVC that supports this?

declarations in for loops

testcase

#include <stdio.h>

int main(int argc, char **argv) {
    char *message = "Built declarations in for loops\n";

    for (const char *p = message; *p; ++p) {
        putchar(*p);
    }
    return 0;
}

benefits

  • Can directly reduce line count without reducing readability
  • Can indirectly make it easier to use const

use blocked by

  • vendor compiler on VMS
  • AIX xlc
  • HP-UX cc (at least with A.06.28.02)
  • first MSVC that supports this?

Beware - gcc 4.7 needed --std=c99 to compile this. Potentially --std=c99 might then warn about gcc-isms, so maybe --std=gnu99 would be better.

member structure initialisers

testcase

#include <stdio.h>

struct message {
    char *action;
    char *target;
};

void greet(struct message mcguffin) {
    printf("%s %s\n", mcguffin.action, mcguffin.target);
}

int main(int argc, char **argv) {
    struct message mcguffin = { .target = "member structure initialisers", .action = "Built" };
    greet(mcguffin);
    return 0;
}

benefits

  • Clearer code.
  • Less chance of errors.
  • Structures can be re-ordered without makework.

use blocked by

Frustratingly these were only recently added to C++, so we couldn't use them in headers, even if we can use them elsewhere.

  • vendor compiler on VMS?
  • AIX xlc
  • HP-UX cc (at least with A.06.28.02)
  • first MSVC that supports this?

Beware - the test case is simplistic. It's quite possible there are bugs on some compilers once one tries to nest these. However, if we find these, it's:

  • Doctor doctor, it hurts when I do this.
  • Well, don't do that then.

and we use old-style syntax instead.

Nice that this feature is, the "lack of C++ support" might already be a big enough blocker to make it less than worthwhile.

variable length arrays

testcase

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    const unsigned int len = strlen(argv[0]);
    char buffer[len];

    memcpy(buffer, argv[0], len);
    printf("Built variable length arrays (as %*s)\n", len, buffer);

    return 0;
}

benefits

  • avoids a call to malloc
  • avoids missing calls to free, particularly on error paths

use blocked by

C11 made this optional. Dammit.

  • vendor compiler on VMS
  • AIX xlc
  • HP-UX cc (at least with A.06.28.02)
  • first MSVC that supports this?