What are the most useful new features in C99?

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.

14488 次浏览

就个人而言,我喜欢 IEC 60559:1989(微处理器系统的二进制浮点算法)和更好的浮点支持。

类似地,设置和查询浮点舍入模式,检查 Nan/Infinity/次正常数等,是非常有用的。

支持从 //开始的一行注释。

stdint.h ,它定义了 int8_tuint8_t等等,不再需要对整数的宽度做出不可移植的假设。

uint32_t truth = 0xDECAFBAD;

变量宏。使得生成带有无限数量参数的样板代码更加容易。

我太习惯打字了

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

在 C + + 中,使用非 C99编译器是一件痛苦的事情,我不得不说

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

支持 inline功能。

我认为新的初始化器机制非常重要。

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

好吧,这不是一个引人注目的例子,但是符号是准确的。可以初始化数组的特定元素和结构的特定成员。

也许这是一个更好的例子——尽管我承认这并不是一个非常引人注目的例子:

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


const char *element_names[] =
{
[Iron]      = "Iron",
[Aluminium] = "Aluminium",
[Beryllium] = "Beryllium",
...
};

复合文字。逐个成员设置结构是如此的‘89;)

您还可以使用它们获取指向具有自动存储持续时间的对象的指针,而无需声明不必要的变量,例如

foo(&(int){ 4 });

而不是

int tmp = 4;
foo(&tmp);

能够在块开始以外的位置声明变量。

可变长度数组:

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]);

布尔型的。

你现在可以这样做:

bool v = 5;


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

将打印

v=1

Unicode 转义序列支持:

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

甚至是字面 Unicode 字符:

printf("日本語\n");

(注意: 可能无法根据您的位置工作; 对不同编码的可移植支持将需要更多的工作)

snprintf()-说真的,能够执行安全的格式化字符串非常有价值。

灵活的阵列成员。

6.7.2.1结构和联合说明符

作为特殊情况,具有多个命名成员的结构的最后一个元素可以 具有不完整的数组类型; 这称为 柔性阵列构件柔性阵列构件。除了两个例外,灵活的数组成员被忽略。首先,构筑物的大小须符合环评报告(登记册编号: 其次,当一个 .(或者 ->)操作符有一个左操作数,它是(指向)一个具有灵活数组成员的结构,并且右操作数命名该成员时,它的行为就好像该成员被替换为最长的数组(具有相同的元素类型) ,这不会使结构大于被访问的对象; 数组的偏移量应该保持为灵活数组成员的偏移量,即使这会与替换数组的偏移量不同。如果这个数组没有元素,那么它的行为就好像它只有一个元素,但是如果尝试访问这个元素或者生成一个指针,那么它的行为就是未定义的 它。

例如:

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


int bufsize = 100;
buffer *b = malloc(sizeof(buffer) + sizeof(int[bufsize]));

十六进制浮点常量(0x1.8p0f)和转换说明符(%a%A)。如果您经常处理低级别的数值细节,那么与十进制文字和转换相比,这些是一个巨大的改进。

在为算法指定常数时,它们使您免于担心四舍五入,并且对于调试低级浮点代码非常有用。

restrict关键字,特别是当你处理数字的时候。

复合字面值,已经提到过,但这里是我引人注目的例子:

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.  */

这是一种清晰的方式来初始化数据,即使它在堆上。没有办法忘记初始化零。