C23 是 ISO C 标准的最新修订版,在 C17 的基础上进行了一些改进和扩展,以下是 C23 的一些新特性。
一、新的类型
1.十进制浮点数类型:引入了_Decimal32、_Decimal64和_Decimal128三种新的十进制浮点数类型,可用于需要精确十进制计算的场景,如金融计算等,能减少二进制浮点数在十进制表示时的舍入误差。
示例:在金融计算中,精确的十进制计算非常重要。例如计算银行利息,假设本金为$1000.00,年利率为3.0%,存期为2年,使用`_Decimal32类型计算利息。
#include <stdio.h>
#include <stdlib.h>
#include <decimal.h>
int main() {
_Decimal32 principal = 1000.00DF;
_Decimal32 interest_rate = 0.03DF;
_Decimal32 interest = principal * interest_rate * 2;
printf("The interest earned is: %f\n", (double)interest);
return 0;
}
这里`DF`是`_Decimal32`类型的后缀,通过这种类型可以更精确地进行十进制小数的运算,避免二进制浮点数转换带来的舍入误差。
2.位精确整数类型:增加了_BitInt(n)和unsigned _BitInt(n)类型用于表示位精确的整数,还添加了_BitInt_max_width宏来表示最大位宽,以及ckd_add()、ckd_sub()、ckd_mul()等用于检查整数操作的宏,有助于进行更精确的位级操作和整数运算。
示例:假设要对一个32位的数据进行位操作,并且希望确保整数类型的位宽精确。
#include <stdio.h>
#include <stdint.h>
#include <stdbit.h>
int main() {
_BitInt(32) bit_int = 0b10101010101010101010101010101010;
// 计算位为1的个数
size_t count_ones = __STDC_count_ones(bit_int);
printf("The number of bits set to 1 is: %zu\n", count_ones);
return 0;
}
这个例子展示了如何使用`_BitInt`类型定义一个位精确的整数,并使用新的位操作函数`__STDC_count_ones`计算其中位为1的个数。
3.nullptr_t 类型:新增了nullptr_t类型,nullptr是其对应的常量,用于更明确地表示空指针,避免了使用NULL可能带来的混淆。
示例:在指针操作中更明确地表示空指针。
#include <stdio.h>
int main() {
int *ptr = nullptr;
if (ptr == nullptr) {
printf("The pointer is null.\n");
}
return 0;
}
这里使用`nullptr`来初始化指针并进行比较,避免了以前`NULL`可能带来的混淆,因为`NULL`在某些情况下可能被定义为0,这可能导致意外的整数和指针的比较。
二、新的常量表示
1.二进制整数常量:允许使用二进制前缀0b或0B来表示整数常量,使位操作的表示更加直观清晰,方便对二进制数据进行处理。
示例:在进行位掩码操作时,二进制常量表示很方便。例如,要设置一个字节的第3位(从0开始计数)和第5位为1。
#include <stdio.h>
int main() {
unsigned char byte = 0b00001010;
printf("The binary value of byte is: %d\n", byte);
return 0;
}
这种二进制表示方式让位操作的意图更加清晰,相比十六进制或十进制表示更直观地展示了位的设置情况。
2.u8 字符常量:引入了使用u8前缀的 UTF-8 字符串字面量,更好地支持 UTF-8 编码的字符处理,便于国际化文本的表示和处理。
示例:在处理UTF - 8编码的文本时,使用`u8`前缀。例如,打印一个包含UTF - 8编码的汉字“好”。
#include <stdio.h>
int main() {
char utf8_char[] = u8"好";
printf("The UTF - 8 character is: %s\n", utf8_char);
return 0;
}
这样可以方便地在C语言中处理UTF - 8编码的字符,对于国际化文本处理很有用。
三、新的初始化方式
空初始化列表:允许使用空的初始化列表{ }来初始化对象,对于具有默认初始值设置的结构体等类型,这种语法更加简洁明了,能更方便地将对象初始化为默认状态。
示例:初始化一个结构体。假设定义一个结构体表示二维坐标点。
struct Point {
int x;
int y;
};
int main() {
struct Point p = {};
printf("The point coordinates are: (%d, %d)\n", p.x, p.y);
return 0;
}
这里使用空初始化列表将结构体`p`的成员初始化为0,这种方式对于有默认初始值设置的结构体更加简洁。
四、新的属性
引入了((nodiscard))、((maybe_unused))、((deprecated))等新属性。例如((nodiscard))属性可用于提醒开发者注意函数返回值的处理,避免忽略重要的返回值而导致潜在错误。
示例(nodiscard属性):定义一个函数用于打开文件并返回文件指针,使用`((nodiscard))`属性提醒开发者处理返回值。
__attribute__((nodiscard)) FILE* open_file(const char* filename) {
FILE* file = fopen(filename, "r");
if (file == NULL) {
perror("Error opening file");
}
return file;
}
int main() {
open_file("test.txt");
// 编译器会警告返回值被忽略,因为函数有((nodiscard))属性
return 0;
}
编译器会警告返回值被忽略,因为函数有((nodiscard))属性 return 0; } ``` - 这可以帮助开发者避免因忽略重要的返回值而导致的潜在错误。
五、新的关键字和语法
_Static_assert 关键字:将_Static_assert从宏转变为关键字,用于在编译时进行静态断言检查,可更方便地验证程序中的条件是否满足,增强了代码的健壮性。
示例:检查结构体大小是否符合预期。假设定义一个结构体用于存储一些数据。
struct Data {
int a;
char b;
};
int main() {
_Static_assert(sizeof(struct Data) >= sizeof(int) + sizeof(char), "Structure size is too small");
return 0;
}
如果结构体大小不符合条件,编译器会在编译时给出错误提示,帮助开发者在早期发现问题。
typeof 关键字:引入了typeof关键字,用于指定类型,可根据已有变量的类型来定义新变量,使代码在处理不同类型但相关的数据时更加灵活和方便。
示例:根据已有的变量类型定义新变量。
#include <stdio.h>
int main() {
int a = 5;
typeof(a) b = 10;
printf("The value of b is: %d\n", b);
return 0;
}
这里`b`的类型根据`a`的类型确定为`int`,`typeof`关键字使代码在处理相关类型的变量时更加灵活。
改进的 auto 关键字:其含义被改变为既可以进行类型推断,同时在与类型一起使用时仍保留其作为存储类说明符的旧含义,但与 C++ 不同的是,C23 仅允许对对象定义进行类型推断,而不能推断函数返回类型或函数参数类型。
示例:在C23中,`auto`可以用于对象定义的类型推断。
int main() {
auto i = 5; // i被推断为int类型
auto arr[] = {1, 2, 3}; // arr被推断为int数组
return 0;
}
与C++不同,C23的`auto`不能推断函数返回类型或函数参数类型。
六、新的预处理器指令
增加了#elifdef、#elifndef、#warning、#embed、__has_embed、__has_include、__has_c_attribute、__va_opt__等预处理器指令和宏。#elifdef和#elifndef可用于创建更灵活的条件编译判断;#warning可用于输出警告信息;#embed可用于将二进制资源嵌入到程序中,方便处理如图片、音频等资源文件。
示例(#embed指令):假设要将一个文本文件的内容嵌入到程序中。
#embed "test.txt"
int main() {
// 在这里可以使用嵌入的文件内容进行后续处理
return 0;
}
具体如何处理嵌入的内容取决于程序的需求,例如可以是配置文件内容、文本资源等。`#embed`指令为处理二进制资源提供了方便的方式。
七、标准库的增强
新函数:添加了memset_explicit()函数用于安全地擦除敏感数据;memccpy()函数用于高效地连接字符串;strdup()和strndup()函数用于分配字符串的副本;mem_alignment()函数用于确定指针的字节对齐;还在<stdbit.h>头文件中添加了一系列位操作的实用函数、宏和类型,如__STDC_count_ones*()、__STDC_count_zeros*()等用于位计数和查找的函数。
示例(memset_explicit函数):安全地擦除敏感数据。假设定义一个数组用于存储敏感信息,如密码。
#include <stdio.h>
#include <string.h>
int main() {
char password[10] = "secret";
memset_explicit(password, 0, sizeof(password));
printf("The password has been erased.\n");
return 0;
}
现有函数的改进:为printf()和scanf()函数家族添加了%b二进制转换说明符,以及为strtol()和wcstol()函数家族添加了对0b和0B二进制转换的支持;同时规定bsearch()、bsearch_s()、memchr()、strchr()等一系列函数在传入const对象时应返回const限定的对象。
示例(printf函数的%b转换说明符):以二进制形式输出整数。
#include <stdio.h>
int main() {
int num = 10;
printf("The binary representation of %d is: %b\n", num, num);
return 0;
}
新增的`%b`转换说明符使输出二进制数据更加方便,无需手动转换。
八、其他特性
更好的数组和 const 支持:提供了对使用const与数组的更好支持,以及对可变修改类型(除了自动变量分配在栈上的可变长度数组之外)的强制支持,使数组的使用更加灵活和安全。
示例:使用`const`修饰数组参数,保证函数内部不会修改数组内容。
void print_array(const int arr[], size_t size) {
for (size_t i = 0; i < size; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {1, 2, 3};
print_array(arr, sizeof(arr)/sizeof(arr[0]));
return 0;
}
这种方式增强了代码的安全性,确保函数不会意外修改传入的数组。
类型兼容性规则的改变:更改了结构、联合和枚举类型的兼容性规则,允许使用相同标签重新声明兼容类型,这在一些复杂的类型定义和重定义场景中提供了更多的灵活性。
示例:重新声明兼容类型的结构体。
struct MyStruct;
struct MyStruct {
int a;
};
int main() {
struct MyStruct s = {1};
return 0;
}
这种规则的改变在复杂的类型定义和重定义场景中提供了更多灵活性,允许开发者在一定条件下重新声明结构体等类型。
标签:C23,include,return,示例,int,特性,C语言,类型,main From: https://blog.csdn.net/easydvlp/article/details/144437239