https://www.runoob.com/cprogramming/c-intro.html
C11(也被称为C1X)指ISO标准ISO/IEC 9899:2011。
在它之前的C语言标准为C99。
新特性:
对齐处理(Alignment)的标准化(包括_Alignas标志符,alignof运算符,aligned_alloc函数以及<stdalign.h>头文件)。
_Noreturn 函数标记,类似于 gcc 的 __attribute__((noreturn))。
_Generic 关键字。
多线程(Multithreading)支持,包括:
_Thread_local存储类型标识符,<threads.h>头文件,里面包含了线程的创建和管理函数。
_Atomic类型修饰符和<stdatomic.h>头文件。
增强的Unicode的支持。基于C Unicode技术报告ISO/IEC TR 19769:2004,增强了对Unicode的支持。包括为UTF-16/UTF-32编码增加了char16_t和char32_t数据类型,提供了包含unicode字符串转换函数的头文件<uchar.h>。
删除了 gets() 函数,使用一个新的更安全的函数gets_s()替代。
增加了边界检查函数接口,定义了新的安全的函数,例如 fopen_s(),strcat_s() 等等。
增加了更多浮点处理宏(宏)。
匿名结构体/联合体支持。这个在gcc早已存在,C11将其引入标准。
静态断言(Static assertions),_Static_assert(),在解释 #if 和 #error 之后被处理。
新的 fopen() 模式,("…x")。类似 POSIX 中的 O_CREAT|O_EXCL,在文件锁中比较常用。
新增 quick_exit() 函数作为第三种终止程序的方式。当 exit()失败时可以做最少的清理工作。
alignas alignof aligned_alloc示例
_Alignas(double) int x; //按 double 类型的对齐方式对齐。
alignas(double) int x;
size_t a = alignof(int); //alignof 返回一个类型的对齐要求
#include <stdlib.h>
int* arr = (int*)aligned_alloc(alignof(int), sizeof(int) * 100);//动态内存分配函数
<stdalign.h> 头文件:
alignas:作为 _Alignas 的同义词存在。
alignof:作为 _Alignof 的同义词存在。
_Noreturn 函数标记示例
_Noreturn 的前身:编译器特定的扩展,例如GCC的__attribute__((noreturn))
_Noreturn void exitWithError(const char* message) {
printf("%s\n", message);
exit(1);//不返回(无限循环或异常终止)
}
_Generic 关键字
_Generic 关键字是一个通用选择表达式,允许基于表达式的类型选择结果。它在某些情况下可以被视为编译时的 switch 语句,专门用于类型。
_Generic 提供了一种强大的机制来在编译时根据类型进行决策,这使得宏可以更加智能和灵活。
#define typename(x) _Generic((x), \
char: "char", \
int: "int", \
double: "double", \
default: "other")
#define square(x) _Generic((x), \
int: (x) * (x), \
float: (x) * (x), \
double: (x) * (x))
多线程(Multithreading)支持
threads.h stdatomic.h _Atomic类型 _Thread_local存储类型
_Thread_local 使得每个线程都有其独特的变量实例。
这意味着对于标记为 _Thread_local 的变量,每个线程都有其独立的副本。
<threads.h> 提供了线程的创建、管理和同步的功能。
thrd_create:创建一个新线程。
thrd_join:等待线程完成。
mtx_t 和相关函数:用于创建和管理互斥锁,用于线程同步。
_Atomic 修饰符和 <stdatomic.h> 头文件提供了一套原子类型和操作
#include <stdio.h>
#include <stdatomic.h>
#include <threads.h>
atomic_int counter = ATOMIC_VAR_INIT(0);
int increment(void* times) {
for(int i = 0; i < *(int*)times; i++) {
atomic_fetch_add(&counter, 1);
}
return 0;
}
int main() {
thrd_t t1, t2;
int times = 100000;
thrd_create(&t1, increment, ×);
thrd_create(&t2, increment, ×);
thrd_join(t1, NULL);
thrd_join(t2, NULL);
printf("Counter value: %d\n", atomic_load(&counter));
return 0;
}
unicode支持,以前需要iconv之类的库
#include <stdio.h>
#include <uchar.h>
int main() {
char16_t utf16_source[] = u"\u03B1"; // Greek letter alpha in UTF-16
char mb_dest[MB_CUR_MAX];
mbstate_t state = { 0 };
size_t bytes = c16rtomb(mb_dest, utf16_source[0], &state);
if (bytes != (size_t)-1) {
mb_dest[bytes] = '\0'; // Null-terminate the multi-byte string
printf("UTF-16 to multi-byte: %s\n", mb_dest);
}
return 0;
}
删除了 gets() 函数,使用更安全的函数gets_s()替代。
从标准输入读取一行文本并将其存储在指定的字符数组中,但它有一个严重的缺陷:gets()函数不检查缓冲区的大小,这可能导致缓冲区溢出。
char buffer[10];
gets(buffer);
gets_s(buffer, sizeof(buffer));
fopen_s(),strcat_s() 等边界检查函数接口,安全函数
以 _s 为后缀,表示它们是标准函数的"安全"版本safe
FILE *file;
errno_t err = fopen_s(&file, "example.txt", "r");
errno_t err = strcat_s(dest, sizeof(dest), src);
errno_t err = strcpy_s(dest, sizeof(dest), src);
c11浮点处理宏
<float.h> 头文件中定义。它们为开发者提供了更细粒度的浮点行为控制。
C11中新增的一些浮点宏的示例:
浮点异常宏:这些宏表示浮点异常的不同类型。当某个浮点操作产生异常时,相应的标志会被设置。
FE_DIVBYZERO: 除以零的异常。
FE_INEXACT: 结果不能精确表示的异常。
FE_INVALID: 例如,对于非数(NaN)的操作产生的异常。
FE_OVERFLOW: 结果超出类型的正范围时的异常。
FE_UNDERFLOW: 结果是正数,但是太小,不能表示为规范化值时的异常。
浮点环境函数:这些函数提供了浮点环境的查询和控制。例如,你可以使用 fesetexceptflag() 和 fegetexceptflag() 来设置和查询浮点异常标志。
浮点舍入模式:这些宏表示不同的舍入模式。
FE_DOWNWARD: 向负无穷大方向舍入。
FE_TONEAREST: 向最近的值舍入,如果有两个相同近的值,则向偶数方向舍入。
FE_TOWARDZERO: 向零舍入。
FE_UPWARD: 向正无穷大方向舍入。
#include <stdio.h>
#include <fenv.h>
#include <float.h>
int main() {
#ifdef FE_UPWARD
if (fesetround(FE_UPWARD)) {//设置舍入模式为向上舍入
printf("Error in setting the rounding mode!\n");
return 1;
}
double x = 2.4;
double y = 2.6;
printf("2.4 rounds to %f\n", nearbyint(x)); // Prints: 2.4 rounds to 3.000000 根据当前的舍入模式来舍入浮点数
printf("2.6 rounds to %f\n", nearbyint(y)); // Prints: 2.6 rounds to 3.000000 根据当前的舍入模式来舍入浮点数
#else
printf("FE_UPWARD is not supported.\n");
#endif
return 0;
}
匿名结构体/联合体、static_assert、fopen(x)
匿名结构体/联合体支持。这个在gcc早已存在,C11将其引入标准。
实际msvc 2003年左右就支持。原理是,编译器没有所谓的struct、union概念,
只是用于解释内存的组织方式,类名只是编译期的标识,
对象名也只在编译期有用,实际汇编层面都是直接地址访问,编译期就根据代码计算好了偏移。
静态断言(Static assertions),_Static_assert(),在解释 #if 和 #error 之后被处理。
这个类似cpp中的static_assert,编译期运行,可验证代码的正确性,不同编译环境的正确性。
新的 fopen() 模式,("…x")。类似 POSIX 中的 O_CREAT|O_EXCL。
如果文件已经存在,fopen()函数会返回NULL,并设置相应的错误。
quick_exit()函数
在C11之前,C语言提供了两种主要的程序终止方式:exit()和abort().
exit()函数会清理所有已注册的退出处理程序、关闭所有打开的文件流等,并终止程序。
abort()则会直接终止程序,而不进行清理。
quick_exit()函数介于两者之间,一个可以快速退出程序但执行一些最小清理工作的方法。
quick_exit()允许注册一些退出处理函数(使用at_quick_exit()函数),这些函数在quick_exit()调用时执行,但不会执行使用atexit()注册的处理函数。
#include <stdio.h>
#include <stdlib.h>
void on_quick_exit_func(void) {
printf("Function executed on quick_exit()\n");
}
void on_exit_func(void) {
printf("Function executed on exit()\n");
}
int main() {
at_quick_exit(on_quick_exit_func);
atexit(on_exit_func);
printf("Main function processing...\n");
if (/* some condition */) {
printf("Quickly exiting...\n");
quick_exit(0); // This will execute only on_quick_exit_func()
}
printf("Normal exit...\n");
exit(0); // This would execute on_exit_func()
return 0;
}