C99中最有用的新特性是什么?

时间:2022-09-06 18:34:05

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.

C99已经存在了10多年,但是支持它的速度很慢,所以大多数开发人员都坚持使用C89。即使是今天,当我在C代码中遇到C99特性时,有时也会感到有些惊讶。

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.

现在大多数主要的编译器都支持C99 (MSVC是一个值得注意的例外,一些嵌入式编译器也落后于C99),我认为使用C的开发人员应该知道C99的可用特性。有些特性是以前从未标准化过的常见特性(例如snprintf),或者是c++所熟悉的(灵活的变量声明放置,或者单行// /注释),但是有些新特性是在C99中首次引入的,很多程序员都不熟悉。

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

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.

作为参考,C99标准(标记为草稿,但据我所知与更新后的标准相同)、新特性列表和GCC C99实现状态。

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

每个答案的一个特征是;请随意留下多个答案。鼓励演示新特性的简短代码示例。

17 个解决方案

#1


72  

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

在c++中,使用非c99编译器是很痛苦的,我不得不说

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

#2


65  

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

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

uint32_t truth = 0xDECAFBAD;

#3


63  

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",
    ...
};

#4


49  

Support for one-line comments beginning with //.

支持以//开头的一行注释。

#5


48  

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

#6


40  

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

能够在一个块的起点以外的位置声明变量。

#7


34  

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

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

#8


31  

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

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

#9


28  

Flexible array members.

灵活的数组成员。

6.7.2.1 Structure and union specifiers

6.7.2.1结构和联盟说明符

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.

作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这被称为一个灵活的数组成员。有两个异常,灵活的数组成员被忽略。首先,结构的大小应等于另一个相同结构的最后一个元素的偏移量,该元素用一个不确定长度的数组替换灵活的数组成员)其次,当a。(- >)算子的左操作数是(指针)结构灵活的数组成员和右操作数名成员,它的行为好像,取而代之的是最长的数组(具有相同元素类型),不会使结构比被访问的对象;阵列的偏移量应该保持柔性阵列成员的偏移量,即使这与替换阵列的偏移量不同。如果这个数组没有元素,那么它的行为就好像它有一个元素,但是如果试图访问该元素或生成一个指针,则行为是没有定义的。

Example:

例子:

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

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

#10


26  

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

复合文字。按成员设置结构为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

insteand的

int tmp = 4;
foo(&tmp);

#11


23  

The bool type.

bool类型。

You can now do something like that:

你可以这样做:

bool v = 5;

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

will print

将打印

1

#12


18  

Support for inline functions.

支持内联函数。

#13


16  

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.

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

#14


15  

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

限制关键字。尤其是当你计算数字的时候……

#15


14  

Unicode escape sequence support:

Unicode转义序列支持:

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

Or even, literal Unicode characters:

甚至是Unicode字符:

printf("日本語\n");

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

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

#16


10  

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.

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

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

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

#17


8  

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

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

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

类似地,设置和查询浮点舍入模式、检查Nan/Infinity/subnormal数等等,都是很好的选择。

#1


72  

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

在c++中,使用非c99编译器是很痛苦的,我不得不说

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

#2


65  

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

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

uint32_t truth = 0xDECAFBAD;

#3


63  

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",
    ...
};

#4


49  

Support for one-line comments beginning with //.

支持以//开头的一行注释。

#5


48  

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

#6


40  

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

能够在一个块的起点以外的位置声明变量。

#7


34  

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

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

#8


31  

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

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

#9


28  

Flexible array members.

灵活的数组成员。

6.7.2.1 Structure and union specifiers

6.7.2.1结构和联盟说明符

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.

作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这被称为一个灵活的数组成员。有两个异常,灵活的数组成员被忽略。首先,结构的大小应等于另一个相同结构的最后一个元素的偏移量,该元素用一个不确定长度的数组替换灵活的数组成员)其次,当a。(- >)算子的左操作数是(指针)结构灵活的数组成员和右操作数名成员,它的行为好像,取而代之的是最长的数组(具有相同元素类型),不会使结构比被访问的对象;阵列的偏移量应该保持柔性阵列成员的偏移量,即使这与替换阵列的偏移量不同。如果这个数组没有元素,那么它的行为就好像它有一个元素,但是如果试图访问该元素或生成一个指针,则行为是没有定义的。

Example:

例子:

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

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

#10


26  

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

复合文字。按成员设置结构为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

insteand的

int tmp = 4;
foo(&tmp);

#11


23  

The bool type.

bool类型。

You can now do something like that:

你可以这样做:

bool v = 5;

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

will print

将打印

1

#12


18  

Support for inline functions.

支持内联函数。

#13


16  

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.

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

#14


15  

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

限制关键字。尤其是当你计算数字的时候……

#15


14  

Unicode escape sequence support:

Unicode转义序列支持:

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

Or even, literal Unicode characters:

甚至是Unicode字符:

printf("日本語\n");

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

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

#16


10  

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.

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

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

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

#17


8  

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

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

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

类似地,设置和查询浮点舍入模式、检查Nan/Infinity/subnormal数等等,都是很好的选择。