C99标准的新特性介绍

C99标准引入了多项新特性,包括复数运算、指定初始化、变长数组、单行注释、柔性数组成员、long long类型、inline函数、bool类型、复合常量、for循环变量初始化及新增头文件,这些特性增强了C语言的灵活性和效率。
摘要由CSDN通过智能技术生成

目录

C语言标准的发展

新特性1、复数(complex)

新特性2、指定初始化(Designated Initializers)

新特性3、变长数组(Variable Length Arrays)

新特性4、单行注释

新特性5、柔性数组成员(Flexible Array Members)

新特性6、long long类型

新特性7、inline函数

新特性8、bool类型

新特性9、复合常量(Compound Literals)

新特性10、for循环变量初始化(for loop intializers)

新特性11、C99新增头文件


 

C语言标准的发展

C语言的发展历史大致上分为4个阶段:Old Style C、C89、C99和C11.

C89是最早的C语言规范,于1989年提出,1990年先由ANSI(美国国家标准委员会,American National Standards Institute)推出ANSI版本,后来被接纳为ISO国际标准(ISO/IEC9899:1990),因而有时也称为C90,最经典的C语言教材[K&R]就是基于这个版本的,C89是目前最广泛采用的C语言标准,大多数编译器都完全支持C89。C99(ISO/IEC9899:1999)是在1999年推出的,加入了许多新的特性,但目前仍没有得到广泛支持,在C99推出之后相当长的一段时间里,连gcc也没有完全实现C99的所有特性。2011年12月8号,ISO 发布了新的 C 语言的新标准——C11,之前被称为C1X,官方名称 ISO/IEC 9899:2011。

C99规范应用于代码编译示例: gcc test.c --std=c99

本文介绍C99相对于C89或者ANSI C的新特性。


新特性1、复数(complex)

complex.h是C标准函数库中的头文件,提供了复数算术所需要的宏定义与函数声明。

#define complex  _Complex
#define _Complex_I  ((const float _Complex)__I__)
#define I  _Complex_I

C99规定了关键字_Complex。因而有3种复数类型:

  • double _Complex
  • float _Complex
  • long double _Complex

次序不是必须遵守的,比如float _Complex也可以写成_Complex float。_Complex_I扩展为类型为const float _Complex的常量值,其值为虚数单位。C99规定complex作为宏扩展为_Complex。但C++未定义complex宏。gcc仅支持complex type,不支持imaginary type。因此宏I扩展为_Complex_I

<complex.h>里面还包含了不少支持复数的数学函数(c打头的就是):

  1. ccos,csin,ctan,cacos,casin,catan:复数域内的三角函数,有对应的f和l版本。
  2. ccosh,csinh,ctanh,cacosh,casinh,catanh:复数域内的双曲函数,有对应的f和l版本。
  3. cexp,clog,cabs,cpow,csqrt:复数域内的指数、对数、绝对值、幂函数,有对应的f和l版本。
  4. carg,cimag,creal,conj,cproj:获取象限角、虚数部分、实数部分、a=x及b=-y、Riemann球上的投影,有对应的f和l版本。
#include <stdio.h>
#include <complex.h>
int main(void)  
{
    double complex cmp = 1.3 + 2.3*I;
    printf("%f + %fi\n", creal(cmp), cimag(cmp));
    return 0;  
} 

新特性2、指定初始化(Designated Initializers)

在初始化结构体和数组时,可以通过指定具体成员名或数组下标来赋初值

要指定数组的索引对应的值,可以在相应的元素值前使用‘[index] =’,index必须是常量表达式例如:

int a[6] = { [4] = 29, [2] = 15 };    等价于: int a[6] = { 0, 0, 15, 0, 29, 0 };

还可以向下面这样初始化:

int a[10] = { [1] = 1, [8 ... 9] = 10 };    等价于:int a[10] = {0, 1, 0, 0, 0, 0, 0, 0, 10, 10};

对于结构体,指定成员名初始化可以使用‘.fieldname=’,例如:

 struct point { int x, y; };

接下来初始化:

struct point p = { .y = yvalue, .x = xvalue };  // 等价于 struct point p = { xvalue, yvalue };

还可以使用冒号:

struct point p = { y: yvalue, x: xvalue };

当然也可以用在union中:

union foo { int i; double d; };

union foo f = { .d = 4 };


新特性3、变长数组(Variable Length Arrays)

C99允许可以定义一个长度为变量的数组(这个数组的长度可以到运行时才决定)

FILE *concat_fopen (char *s1, char *s2, char *mode)
{
       char str[strlen (s1) + strlen (s2) + 1];
       strcpy (str, s1);
       strcat (str, s2);
       return fopen (str, mode);
}

也可以在结构体或是联合中使用VLA:

void foo (int n)
{
      struct S { int x[n]; };
}

你可以使用alloca函数实现类似的功能,但是alloca函数并不是都实现,从另一角度而言,VLA更加的优秀

也可以使用VLA作函数参数:

struct entry tester (int len, char data[len][len])
{
       /* ... */
}

示例代码:

#include<stdio.h>
void func(int n)
{
    int vla[n];
    printf("%d\n", sizeof(vla));
}
int main()  
{
    func(4);
    return 0;  
}  

新特性4、单行注释

gcc支持像C++风格的注释,以‘//’开头直到一行的结束,很多其他支持C99的C编译器都支持,但是c99之前的版本有可能不支持

新特性5、柔性数组成员(Flexible Array Members)

在日常的编程中,有时候需要在结构体中存放一个长度动态的字符串,一般的做法,是在结构体中定义一个指针成员,这个指针成员指向该字符串所在的动态内存空间,例如:

typedef struct test  
{  
       int a;  
       double b;  
       char *p;  
}

p指向字符串。这种方法造成字符串与结构体是分离的,不利于操作。如果把字符串跟结构体直接连在一起,不是更好吗?于是,可以把代码修改为这样:

char a[] = "hello world";
test *stpTest = (test *)malloc(sizeof(test) + strlen( a ) + 1 );
strcpy(stpTest + 1, a );

这样一来,( char* )(stpTest + 1 )就是字符串"hello world"的地址了。这时候p成了多余的东西,可以去掉。但是,又产生了另外一个问题:老是使用( char* )((stpTest + 1 )不方便。如果能够找出一种方法,既能直接引用该字符串,又不占用结构体的空间,就完美了,符合这种条件的代码结构应该是一个非对象的符号地址,在结构体的尾部放置一个0长度的数组是一个绝妙的解决方案。不过,C/C++标准规定不能定义长度为0的数组,因此,有些编译器就把0长度的数组成员作为自己的非标准扩展。
在讲述柔性数组成员之前,首先要介绍一下不完整类型(incomplete type)。不完整类型是这样一种类型,它缺乏足够的信息例如长度去描述一个完整的对象,它的出现反映了C程序员对精炼代码的极致追求,这种代码结构产生于对动态结构体的需求。

鉴于这种代码结构所产生的重要作用,C99甚至把它收入了标准中。C99使用不完整类型实现柔性数组成员,在C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组(flexible array)成员(也叫伸缩性数组成员),但结构中的柔性数组成员前面必须至少一个其他成员。柔性数组成员允许结构中包含一个大小可变的数组。柔性数组成员只作为一个符号地址存在,而且必须是结构体的最后一个成员,sizeof 返回的这种结构大小不包括柔性数组的内存。柔性数组成员不仅可以用于字符数组,还可以是元素为其它类型的数组。

用法:包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。柔性数组的使用请看下面的例子:

typedef struct test
{
	int a;
	double b;
	char c[0];
};

有些编译器会报错无法编译可以改成:
typedef struct test
{
	int a;
	double b;
	char c[];
};

 

通过如下表达式给结构体分配内存: test *stpTest = (test *)malloc(sizeof(test)+100*sizeof(char));

c就是一个柔性数组成员,如果把stpTest指向的动态分配内存看作一个整体,c就是一个长度可以动态变化的结构体成员,柔性一词来源于此。c的长度为0,因此它不占用test的空间,同时stpTest->c就是“hello world”的首地址,不需要再使用( char * )( stpTest + 1 )这么丑陋的代码了。那个0个元素的数组没有占用空间,而后我们可以进行变长操作了。这样我们为结构体指针c分配了一块内存。用stpTest->c[n]就能简单地访问可变长元素。

应当尽量使用标准形式,在非C99的场合,可以使用指针方法。需要说明的是:C89不支持这种东西,C99把它作为一种特例加入了标准。但是,C99所支持的是incomplete type,而不是zero array,形同int a[0];这种形式是非法的,C99 支持的形式是形同int a[];只不过有些编译器把int a[0];作为非标准扩展来支持,而且在C99 发布之前已经有了这种非标准扩展了,C99 发布之后,有些编译器把两者合而为一了。

柔性数组介绍原文链接:https://blog.csdn.net/ce123_zhouwei/article/details/8973073

 


新特性6、long long类型

C99支持64位整型,使用long long int 或使用unsigned long long int,将整型常量声明为long long int,在整数的后面加上‘LL’,若为unsigned long long int,则加上‘ULL’

新特性7、inline函数

c/c++中的inline,使用在函数声明处,表示程序员请求编译器在此函数的被调用处将此函数实现插入,而不是像普通函数那样生成调用代码(申请是否有效取决于编译器)。一般地说,这样作的优点是省掉了调用函数的开销;缺点则是可能会增加代所生成目标代码的尺寸

实际上,即使没有手工指定inline函数,编译器一般还会选择一些代码量较小但使用频繁的函数作为inline函数,以此作为性能优化的途径之一。

和带参宏定义(Parameterized Macro)相比,具备以下优点:

  • 参数类型检查:宏定义中所使用的参数仅仅是在宏定义中被替换,不进行任何的类型检查

  • 返回值:宏定义中无法使用return返回

  • 便于调试

新特性8、bool类型

以前都是自己写#define TRUE 1, #define FALSE  0 或者 enum boolean之类的宏,现在可以使用<stdbool.h>的bool类型。

新特性9、复合常量(Compound Literals)

简单来说复合常量就是允许你定义一个匿名的结构体或数组变量。如:

struct foo {int a; char b;} structure;

structure = ((struct foo) {x + y, 'a'});

等价于:

{

    struct foo temp = {x + y, 'a'};
           structure = temp;
        }

也可以创建一个数组:

char **foo = (char *[]) { "x", "y", "z" };

更多实例:

static struct foo x = (struct foo) {1, 'a'};
static int y[] = (int []) {1, 2, 3};
static int z[] = (int [3]) {1};
//等价于下面的代码:
static struct foo x = {1, 'a'};
static int y[] = {1, 2, 3};
static int z[] = {1, 0, 0};


新特性10、for循环变量初始化(for loop intializers)

C99引入了C++中的for循环变量初始化方式:

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

除了写起来方便以外,循环变量的生存周期也被最小化了。这也顺便杜绝了那种把循环变量带到循环外头继续用的恶习

新特性11、C99新增头文件

  <complex.h>          支持复杂算法
  <fenv.h>               给出对浮点状态标记和浮点环境的其他方面的访问
  <inttypes.h>          定义标准的、可移植的整型类型集合,也支持处理最大宽度整数的函数
  <iso646.h>            首先在此1995年第一次修订时引进,用于定义对应各种运算符的宏
  <stdbool.h>           支持布尔数据类型类型,定义宏bool,以便兼容于C++
  <stdint.h>             定义标准的、可移植的整型类型集合,该文件包含在<inttypes.h>中
  <tgmath.h>           定义一般类型的浮点宏
  <wchar.h>             首先在1995年第一次修订时引进,用于支持多字节和宽字节函数
  <wctype.h>           首先在1995年第一次修订时引进,用于支持多字节和宽字节分类函数


本文转账自:http://www.cnblogs.com/archimedes/p/c99-new-feature.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值