本文将持续介绍C99中新增特性,由于工作环境在C89,因此学习C99中新增特性,本文将持续更新.

C99中的布尔值

C99提供了_Bool型,所以在这一版本中,布尔变量可以声明为_Bool flag;
除了_Bool类型的定义,C99还提供了一个新的头该头文件提供了bool宏,用来代表_Bool,同时还提供了true 和 false分别代码1和0.
示例程序:

#include <stdio.h>
int main()
{
    _Bool flag = 0;
    printf("%d\n",flag);
    return 0;
}

输出0

#include <stdio.h>
#include <stdbool.h>

int main()
{
    bool flag = false;
    printf("%d\n",false);
    printf("%d\n",true);
    printf("%d\n",flag);
    return 0;
}

输出010(带换行)

for语句

在C99中,for语句的第一个表达式可以替换为一个声明,这一特性使得程序员可以声明一个用于循环的变量:
for(int i = 0;i < n;i++)
变量i不需要再该语句前进行声明,事实上,如果变量i在之前已经声明过,这个语句创建一个新的i且作用域仅在循环内。

C99中的整数类型

C99中提供了两个额外的标准整数类型long long int 和unsigned long long int。增加这两种类型有两个原因一是为了满足对超大整数的需求,二是支持64位运算的新处理器的能力。long longint的范围是-2的63次方到2的63次方-1,unsigned long long int范围是0-2的64方-1.

C99中的整数常量

在C99中,以LL或ll结尾的证书常量是long long int型,如果在LL或ll前面或者后面加字母U(u),则该整数常量为unsigned long long int 型。

指定初始化式

C99中的指定初始化式可以使赋值语句变的更短、更易读,赋值的顺序不再是一个问题。
例如int a[15]={[2]=29,[9]=81,[1]=25};

受限指针restrict

c99中新增加了一个类型定义,就是restrict。
概括的说,关键字restrict只用于限定指针;该关键字用于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,即不存在其它进行修改操作的途径;这样的后果是帮助编译器进行更好的代码优化,生成更有效率的汇编代码。
一个简单的例子:

int foo (int* x, int* y) {
    *x = 0;
    *y = 1;
    return *x;
}

很显然函数foo()的返回值是0,除非参数x和y的值相同。可以想象,99%的情况下该函数都会返回0而不是1。然而编译起必须保证生成100%正确的代码,因此,编译器不能将原有代码替换成下面的更优版本

int f (int* x, int* y) {
    *x = 0;
    *y = 1;
    return 0;
}

现在我们有了restrict这个关键字,就可以利用它来帮助编译器安全的进行代码优化了

int f (int *restrict x, int *restrict y) {
    *x = 0;
    *y = 1;
    return *x;
}

此时,由于指针 x 是修改 *x的唯一途径,编译起可以确认 “*y=1; ”这行代码不会修改 *x的内容,因此可以安全的优化为

int f (int *restrict x, int *restrict y) {
    *x = 0;
    *y = 1;
    return 0;
}

说到受限指针,不得不说memcpy和memove两个函数。

void *memcpy( void * restrict dest , const void * restrict src, size_t n) 
void *memmove(void *dest, const void *src, size_t n);


第一种情况下,拷贝重叠的区域不会出现问题,内容均可以正确的被拷贝。
第二种情况下,问题出现在右边的两个字节,这两个字节的原来的内容首先就被覆盖了,而且没有保存。所以接下来拷贝的时候,拷贝的是已经被覆盖的内容,显然这是有问题的。
memcpy的大致实现:

#include <stddef.h> /* size_t */
void *memcpy(void *dest, const void *src, size_t n)
{
    char *dp = dest;
    const char *sp = src;
    while (n--)
        *dp++ = *sp++;
    return dest;
}

memmove的大致实现:

#include <stddef.h> /* for size_t */
#include <stdlib.h> /* for memcpy */

void *memmove(void *dest, const void *src, size_t n)
{
    unsigned char tmp[n];
    memcpy(tmp,src,n);
    memcpy(dest,tmp,n);
    return dest;
}

由于memmove先将内容拷贝到tmp中,因此速度较memcpy慢,
参考memmove中unsigned char tmp[n]这种形式因此,需要C99中变长数组支持。

变长数组

The concept of a variable-length array (VLA) was introduced in the 1999 revision of the C standard, C99. VLAs involve several changes to the language syntax to facilitate passing them to functions.

Any array whose number of elements is not a constant, will be implemented as a VLA by a C99 compiler.

#define a 1
int b = 1;
const int c = 1;

double foo[a];  /* normal fixed-length array */
double bar[b];  /* variable-length array */
double baz[c];  /* variable-length array */

变长数组形式参数

由于C99支持变长数组,因此如下形式是正确的。
int sum_array(int n,int a[n])
需要注意的是函数参数中int n必须在int a[n]前面。

在数组参数声明中使用static

int sum_array(int a[static 3],int n)
{
    
}

在上面的代码中static不会对程序的行为有任何影响,static放在数字3前面表明数组a的长度至少可以保证是3.C编译器可以据此生成更快的指令来访问数组。
如果static应用于多维数组,则仅可用于第一维,如指定二维数组的行数。

复合字面量

在上面的例子中函数int sum_array(int a[static 3],int n),我们调用函数时可以如此调用:

int b[]={3,0,3,4,1};
sum_array(b,5);

C99支持如下形式调用:

total = sum_array((int []){3,0,3,4,1},5);

指向复合常量的指针

复合常量是C99的一个特性,可以用于创建没有名称的数组。例如:

int *p =(int []){3,0,3,4,1};

p指向了一个拥有5个元素的数组的第一个元素,这个数组包括5个整数3 0 3 4 1.使用复合字面量可以减少一些麻烦,我们不再需要声明一个数组变量,然后使用指针p指向数组的第一个元素。