C99中最有用的新功能是什么?[关闭]


78

C99已经存在了10多年,但是对其的支持却来得很缓慢,因此大多数开发人员都对C89持保留态度。即使在今天,当我在C代码中遇到C99功能时,有时我还是会感到有些惊讶。

既然大多数主要的编译器都支持C99(MSVC是一个显着的例外,并且某些嵌入式编译器也很落后),我觉得使用C的开发人员可能应该知道他们可以使用哪些C99功能。一些功能只是以前从未标准化的通用功能(snprintf例如,),或者是C ++所熟悉的(灵活的变量声明放置,或单行//注释),但是其中一些新功能最初是在C99中引入的,并且许多程序员都不熟悉。

您发现C99中最有用的新功能是什么?

作为参考,C99标准(标记为草案,但据我所知与更新的标准相同),新功能列表以及GCC C99实施状态

请为每个答案提供一项功能;随时留下多个答案。鼓励使用简短的代码示例演示新功能。


2
对于人们在C99中讨厌的功能,应该有一个类似的Wiki !
Alok Singhal 2010年

嗯,有一个关于有害的或不支持的C99功能的问题stackoverflow.com/questions/1898890/...
布赖恩·坎贝尔

谢谢。您应该更改链接文本以指示它是草稿,而不是实际的标准,并在链接至n1256时链接它:-)。顺便说一句,看着gcc.gnu.org/c99status.html,我不会说大多数C99受gcc支持。而且由于gcc是使用最广泛的C编译器之一,...
Alok Singhal 2010年

2
请注意,在嵌入式处理器领域,可能仍无法很好地支持C99。
Craig McQueen 2010年

1
@Alok我将大多数功能称为那种支持级别;我想它取决于您如何定义它,但是我认为人们想要使用的大多数重要功能都受支持,而忽略了一些库问题。@Craig很公平,添加了关于嵌入式编译器的免责声明。
布莱恩·坎贝尔

Answers:



72

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

uint32_t truth = 0xDECAFBAD;

19
自DE:AD:BE:EF:CA:FE以来的最佳十六进制词组。
凯文·L.2010年

十足糟应该是什么意思?
Pacerier

5
@Pacerier这只是用于说明示例的任意十六进制常量。但是对于幽默价值,它表示“聋子不好”,这意味着脱咖啡因的咖啡不如真咖啡。
Brian Campbell

5
好吧,我不能跟他们的幽默...
Pacerier

66

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

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

6
这是一个引人注目的示范。有大量的枚举表和对应的字符串表。
u0b34a0f6ae 2011年

5
@ColeJohnson:如果仔细看一下第二个示例,您会发现初始化列表的顺序是不正确的-但可以正常工作。仅凭定义就不能做到这一点。第一个示例仅初始化数组的索引4。也不能仅仅通过定义来完成。
乔纳森·勒夫勒

2
还应该注意的是,多次使用同一索引将覆盖第二种用法,第一次使用-请参阅我的问题,例如:stackoverflow.com/questions/16742467/…–
约翰尼

51

可变长度数组:

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

3
您真的认为VLA阵列很棒吗?C11使它们可选。
Z玻色子2015年

3
只是不要忘记应用输入卫生措施以防止堆栈溢出(如果x为负,则防止堆栈损坏):if (x < 0) x = 0; else if (x > 1024) x = 1024;
Andrew D'Addesio


41

能够在块的起始位置以外的其他位置声明变量。


2
我不太喜欢这个。在我看来,变量不仅应在需要时引入作用域,而且在不需要时应从作用域中除去。变量中段几乎总是在范围内徘徊,比它们应有的更长。
超级猫

5
@supercat您愿意int a; int b; a = f(); b = g();int a = f(); int b = g();?在变量初始化位置附近声明变量对于减少错误非常重要。
Ryan Haining 2014年

@RyanHaining:或者,创建一个嵌套的范围块(我只是希望有上面写着:“此块只是作用域”标准语法,可以使用容易使用空宏用于该目的,但似乎有点难看
超级猫

@supercat有一个标准语法:/* this block is just for scoping */ { } /* scope */:)
Cacahuete Frito

@CacahueteFrito:我认为if(1) /* Scoping block */ { }要好一些,但是更大的问题是作用域和控制是正交的概念。虽然有时在一个循环中对事物进行范围界定是有意义的,但使事物仅在循环中进行范围界定通常更为有用,并且在很多情况下,代码将创建对象来保存创建对象所需的值。寿命更长的对象,但是从那以后(例如,float dx=x2-x1, dy=y2-y1; float distance = sqrt(dx*dx+dy*dy);没有理由将dx和dy保持在范围内。)
supercat

36

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



29

复合文字。逐个成员设置结构是'89;)

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

foo(&(int){ 4 });

指示

int tmp = 4;
foo(&tmp);

它应该依次为4分配堆栈内存吗?
AlphaGoku

29

灵活的数组成员。

6.7.2.1结构和联合规范

作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这称为“灵活数组成员”。除两个例外,灵活数组成员将被忽略。首先,结构的大小应等于其他结构相同的最后一个元素的偏移量,该结构用没有指定长度的数组替换柔性数组成员)第二,当.(或->)运算符的左操作数是指向具有灵活数组成员的结构的指针(指向该结构),并且该成员的右操作数名称相同,它的行为就好像该成员被最长的数组(具有相同元素类型)替换,而不会使结构大于要访问的对象;数组的偏移应保留为灵活数组成员的偏移,即使这与替换数组的偏移不同。如果此数组不包含任何元素,则其行为就好像它具有一个元素,但是如果尝试访问该元素或生成一个指针经过该元素,则该行为未定义。

例:

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

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

2
+1终于使这个洁净。我见过的每个TCP / IP套接字代码中都有它。
slebetman 2010年

实际上,与使用buf [1]并从malloc大小中减去1的常见技巧相比,我认为它更加洁净。这个技巧可能很常见,但是我将其视为未定义行为(正确的做法是使用buf [MAX_SIZE]并从malloc大小中减去MAX_SIZE),因为编译器生成的索引代码可能取决于buf []的感知大小。
超级猫

25

布尔类型。

您现在可以执行以下操作:

bool v = 5;

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

将打印

1


不,bool在C99中,强制分配给变量的值始终为1或0。因此,如果您的代码在C99编译器和较旧的具有“模拟”bool类型的编译器上运行,则必须格外小心。使用!!将使便携式。v = !!5;具有相同的语义,但不易读。
PatrickSchlüter2014年

分配像6这样的偶数时,它会打印1吗?
phuclv

是。任何非零值港岛线由1代替
帕特里克SCHLÜTER

18

支持inline功能。


实际上,GCC在确定要内联的函数时通常会忽略inline关键字,并根据其自身的启发式自动内联事物,除非您强迫它这样做。
daf 2010年

7
daf:inline仍然有帮助,因为它允许您在多个翻译单元中定义函数-因此您可以将其放在头文件中,从而提供跨模块内联的机会。
caf 2010年

@dafinline __attribute__((force_inline))
Cole Johnson

18

复合文字,已经提到过,但这是我引人注目的示例:

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

这是初始化数据的清晰方法,即使它在堆上也是如此。没有办法忘记对某些东西进行零初始化。



15

Unicode转义序列支持:

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

甚至是文字Unicode字符:

printf("日本語\n");

(注意:可能无法使用,具体取决于您的区域设置;对不同编码的可移植支持将需要更多的工作)


12

十六进制浮点常量(0x1.8p0f)和转换说明符(%a%A)。如果您经常处理低级的数字细节,则相对于十进制文字和转换而言,它们是巨大的改进。

当为算法指定常量时,它们使您免于舍入的麻烦,并且对于调试低级浮点代码非常有用。


3
是的,前几天我只是在尝试向某人解释浮点数在另一个Stack Overflow问题中如何工作时使用了这些。
Brian Campbell 2010年

9

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

同样,设置和查询浮点舍入模式,检查Nan / Infinity /次正规数等也非常有用。


好吧,MS不喜欢这样做
Cole Johnson

对于舍入模式的概念,C并不是真正设计得很好的,因为它们意味着浮点数学需要被视为具有副作用。拥有比“希望最好的”浮点语义更好的东西很有用,但我不知道有多少实现真正满足附件F的所有要求
。– supercat
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.