-
Notifications
You must be signed in to change notification settings - Fork 559
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...
-
xlc
on AIX officially got c99 support in July 2002 with version V6.0 ---qlanglvl=stdc99
(at that time not the default) - HP
aC/C++
references C99 support in documents from 2004 ---AC99
- Sun C 5.9 (from 2010) can enable C99 features with
-xc99
. Current Solaris compilers default to C11. - MSVC since Microsoft Visual C++ 2013 Express x86 (VC12.0)
- VMS vendor compiler has most of the syntax, but library support has only arrived incrementally
- MINIX has clang 3.6
- QNX Neutrino 6.5.0 has gcc 4.4.2
As Craig A. Berry notes:
Last year there was a patch that finally introduced stdint.h, moving to it a lot of things that had been in inttypes.h and supplying a lot of things that were missing. fpclassify() got some attention and the %z format specifier finally became available. va_copy is still not available, but is expected to be on x86_64.
Open questions are:
- Supported by all compilers we tested.
#include <stdio.h>
int main(int argc, char **argv) {
// Comment
puts("Built "
// Comment
"// comments");
return 0;
}
None have been clearly explained.
- Supported by all compilers we tested.
#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;
}
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 struct
s or union
s, whereas the hack we have works as the last member within nested aggregates.
(even if slow, and just for arithmetic and bitwise operators)
- Needs Microsoft Visual C++ 2003 Toolkit x86 (VC7.1)
#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;
}
- Certain calculations can be expressed directly
- Needs Microsoft Visual C++ 2005 Express x86 (VC8.0)
#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;
}
- very useful for making
sprintf
style defines actually DWIM with PERL_NO_CONTEXT
- Needs Microsoft Visual C++ 2013 Express x86 (VC12.0)
#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;
}
- 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
- Needs Microsoft Visual C++ 2013 Express x86 (VC12.0)
- 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.
#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;
}
- Can directly reduce line count without reducing readability
- Can indirectly make it easier to use const
- Needs Microsoft Visual C++ 2013 Express x86 (VC12.0)
- Only recently added to C++, so we can't use them in headers
#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;
}
- Clearer code.
- Less chance of errors.
- Structures can be re-ordered without makework.
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.
- C11 made this optional.
- Not supported by any MSVC.
- We can't use these :-(
- Tomasz Konojacki suggests that we set
gcc
to treat them as errors, if viable.
#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;
}
- avoids a call to
malloc
- avoids missing calls to
free
, particularly on error paths
- undefined behaviour if you overflow the stack
- no portable way to figure out your stack size