C语言标准
C语言从其诞生至今,经历了多个标准的更新,主要标准包括:
-
C89/C90 (ANSI C / ISO/IEC 9899:1990):这是C语言的第一个官方标准,由ANSI于1989年发布,后被ISO采纳为国际标准,发布于1990年。它为C语言的语法、库函数等方面设定了基础规范,广泛被采用并成为后续标准的基础。 -
C99 (ISO/IEC 9899:1999):发布于1999年,C99标准在C89的基础上进行了大量扩展,引入了如可变长度数组(VLAs)、限制指针(restrict)、内联函数、复数类型、新的整数类型(如_Bool)、改进的预处理功能等特性。 -
C11 (ISO/IEC 9899:2011):发布于2011年,C11标准在C99基础上进一步完善,加入了对多线程编程的支持(通过库)、增强了Unicode支持(通过)、引入了原子操作和线程内存模型、静态断言、匿名结构和联合、宏默认参数等新特性,并提高了语言的安全性。 -
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.h和stdlib.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关键字告诉编译器,dest和src指向的内存区域不会重叠,这使得编译器可以进行更多的优化。
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关键字定义复数类型,并通过creal和cimag函数获取实部和虚部。
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中的库创建和等待一个线程完成。
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;
- }
复制代码
通过头文件,可以使用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;
- }
复制代码
通过头文件,可以使用原子类型和操作进行线程安全的计数,这里展示了如何在多线程环境下安全地增加一个计数器的值。 |