C语言C99标准、C11标准新增加的特性

举报
DS小龙哥 发表于 2024/06/07 13:37:30 2024/06/07
【摘要】 C89是C语言的第一个官方国际标准,正式名称为ISO/IEC 9899:1990。它是在1989年由美国国家标准协会(ANSI)制定并发布的,故得名C89,随后在1990年被国际标准化组织(ISO)采纳,成为国际标准。C89标准定义了C语言的基础语法、关键字、数据类型,并引入了标准库函数,比如stdio.h和stdlib.h等,确立了C语言的基本形态。它的特点是简洁、可移植性强且易于理解,成为了后

C语言标准

C语言从其诞生至今,经历了多个标准的更新,主要标准包括:

  1. C89/C90 (ANSI C / ISO/IEC 9899:1990) :这是C语言的第一个官方标准,由ANSI于1989年发布,后被ISO采纳为国际标准,发布于1990年。它为C语言的语法、库函数等方面设定了基础规范,广泛被采用并成为后续标准的基础。
  2. C99 (ISO/IEC 9899:1999) :发布于1999年,C99标准在C89的基础上进行了大量扩展,引入了如可变长度数组(VLAs)、限制指针(restrict)、内联函数、复数类型、新的整数类型(如_Bool)、改进的预处理功能等特性。
  3. C11 (ISO/IEC 9899:2011) :发布于2011年,C11标准在C99基础上进一步完善,加入了对多线程编程的支持(通过<threads.h>库)、增强了Unicode支持(通过<uchar.h>)、引入了原子操作和线程内存模型、静态断言、匿名结构和联合、宏默认参数等新特性,并提高了语言的安全性。
  4. C18 (ISO/IEC 9899:2018) :发布于2018年,这个版本主要是对C11标准的小幅修订和澄清,没有引入重大的新特性,主要目的是解决C11标准中发现的问题和歧义,提高标准的清晰度和一致性。C18有时也被视为C11的一个修正版。

目前,最新的官方标准是C18,但需要注意的是,并非所有的编译器都已经完全实现了最新标准的所有特性,开发者在编写代码时应考虑目标编译器的实际支持情况。

C89标准(也称为C90标准)

C89是C语言的第一个官方国际标准,正式名称为ISO/IEC 9899:1990。它是在1989年由美国国家标准协会(ANSI)制定并发布的,故得名C89,随后在1990年被国际标准化组织(ISO)采纳,成为国际标准。C89标准定义了C语言的基础语法、关键字、数据类型,并引入了标准库函数,比如stdio.hstdlib.h等,确立了C语言的基本形态。它的特点是简洁、可移植性强且易于理解,成为了后续C语言教材和实现的基础。

C99标准(ISO/IEC 9899:1999)

C99是C语言的一个重要更新,发布于1999年。它是C89标准的后续版本,引入了许多新特性和改进,旨在适应不断发展的编程需求和技术环境。C99标准增加了诸如限制指针(restrict)、内联函数、可变长度数组(VLAs)、复数类型(_Complex)、新的整型常量(如_Bool)、改进的浮点数处理以及对编译器限制的放宽等特性。此外,C99还引入了//风格的单行注释,使代码更加易读。尽管C99引入了许多现代化的特性,但直到今天,并非所有编译器和开发环境都完全支持C99的所有特性。

下面是一些C99标准新增特性及其示例代码:

1. restrict指针

void copy_memory(void *restrict dest, const void *restrict src, size_t n) {
    for(size_t i = 0; i < n; ++i) {
        ((char*)dest)[i] = ((const char*)src)[i];
    }
}

在这个例子中,restrict关键字告诉编译器,destsrc指向的内存区域不会重叠,这使得编译器可以进行更多的优化。

2. 内联函数



inline int add(int a, int b) {
    return a + b;
}

使用inline关键字建议编译器直接将函数体插入每次调用处,以减少函数调用的开销。

3. 可变长度数组(VLAs)



#include <stdio.h>
​
int main() {
    int n = 5;
    int arr[n]; // 数组大小在运行时决定
    for(int i = 0; i < n; ++i) {
        arr[i] = i;
    }
    for(int i = 0; i < n; ++i) {
        printf("%d ", arr[i]);
    }
    return 0;
}

这里,数组arr的大小是在运行时根据变量n的值确定的。

4. 复数类型



#include <complex.h>
#include <stdio.h>
​
int main() {
    double complex z = 3.0 + 4.0*I;
    printf("The complex number is: %f + %fi\n", creal(z), cimag(z));
    return 0;
}

使用_Complex关键字定义复数类型,并通过crealcimag函数获取实部和虚部。

5. 指定成员初始化器



struct Person {
    char name[20];
    int age;
};
​
int main() {
    struct Person p = {.name = "Alice", .age = 30};
    printf("Name: %s, Age: %d\n", p.name, p.age);
    return 0;
}

在结构体初始化时,可以直接指定成员的名字进行初始化,提高了代码的清晰度。

6. 对齐处理



#include <stdalign.h>
​
alignas(16) char buffer[100]; // 确保buffer对齐在16字节边界上
​
int main() {
    // ... 使用对齐后的buffer
    return 0;
}

使用_Alignas关键字可以指定变量或类型的对齐要求。

C11 (ISO/IEC 9899:2011)

C11标准引入了若干新特性,以下是一些主要特性的示例代码:

1. 多线程支持



#include <threads.h>
#include <stdio.h>
​
void thread_function(void* arg) {
    printf("Hello, World! from thread\n");
}
​
int main() {
    thrd_t t;
    if(thrd_create(&t, thread_function, NULL) == thrd_success) {
        thrd_join(t, NULL); // 等待线程结束
    }
    printf("Hello, World! from main thread\n");
    return 0;
}

这段代码展示了如何使用C11中的<threads.h>库创建和等待一个线程完成。

2. Unicode支持



#include <stdio.h>
#include <uchar.h>
​
int main() {
    char32_t unicode_char = U'ação'; // 使用UTF-32编码存储Unicode字符
    printf("Unicode character: %C\n", unicode_char);
    return 0;
}

通过<uchar.h>头文件,可以使用Unicode字符,并利用宽字符输出函数打印。

3. 静态断言

#include <assert.h>

#define MAX_SIZE 100

void func(int size) {
    _Static_assert(size <= MAX_SIZE, "size exceeds maximum allowed");
    // ... 函数逻辑
}

int main() {
    func(99); // 正常执行
    // func(101); // 如果取消注释,编译时会报错
    return 0;
}

_Static_assert允许在编译时验证条件,如果条件不满足,则编译失败。

4. 匿名结构与联合

#include <stdio.h>

struct Info {
    int id;
    union {
        char name[20];
        // 可以有其他成员
    };
};

int main() {
    struct Info info = {1, "Alice"};
    printf("ID: %d, Name: %s\n", info.id, info.name);
    return 0;
}

C11允许在结构体内部直接定义匿名的联合,简化了结构体的设计。

5. 原子类型和操作



#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>

atomic_int counter = ATOMIC_VAR_INIT(0);

void increment(void* arg) {
    for(int i = 0; i < 100000; ++i) {
        atomic_fetch_add_explicit(&counter, 1, memory_order_relaxed);
    }
}

int main() {
    thrd_t threads[10];
    for(int i = 0; i < 10; ++i) {
        thrd_create(&threads[i], increment, NULL);
    }
    for(int i = 0; i < 10; ++i) {
        thrd_join(threads[i], NULL);
    }
    printf("Counter: %d\n", atomic_load_explicit(&counter, memory_order_relaxed));
    return 0;
}

通过<stdatomic.h>头文件,可以使用原子类型和操作进行线程安全的计数,这里展示了如何在多线程环境下安全地增加一个计数器的值。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。