Elliot's Blog 您所在的位置:网站首页 c99什么时候开始 Elliot's Blog

Elliot's Blog

2024-02-26 12:08| 来源: 网络整理| 查看: 265

现代 C99, C11 标准下的 C 语言编程 一、摘要

一直以来,我们所学习的 C 语言大多是 ANSI-C 标准,也就是后来被标准化的 C89 标准。在 1999 年发布的 C99 和 2011 年发布的 C11 标准在此之上,引入了许多新的特性,也解决了许多问题。因此,随着标准的发布,我们的 C 语言规范和写法也要发生相应的变化。

C++ 同样也发布了 C++99,C++11,C++14 甚至 C++17 规范。从变化上看,C++11 规范之后的 C++ 语言已经焕然一新,引入了大量非常现代化的特性。C 语言规范的最大的变化则发生在 C99 规范之中。其后的 C11 虽然也有一些特性,但更多的算是为了于 C++ 同步而引入的新特性。

目前的 GCC 和 Clang 编译器都已经完整支持 C99 和 C11 的特性,默认都是支持 C11 规范。如果需要显式指定的时候,则在编译时加入 -std=c99 或者 -std=c11 即可。

本文将介绍这两个协议下带来的新特性,和我们新的编码习惯的变化。

二、新的基本数据类型规范

在 C99 规范中,有着大量对于新的数据类型的定义和补充。这是非常有必要的,原先的 int,long 等变量基本类型在不同架构的机器上,会有不同的长度,往往会导致不可预期的问题。64 位数值、布尔类型和复数类型的缺失、以及 Unicode 的缺失也阻碍了 C 语言在现代的进一步发展。因此,C99 类型中带来了大量编码类型的变化。

2.1 数值类型

我们经常因为数据类型在不同架构机器上的不同表现,而感到困扰。因此在 C99 规范中,引入了标准的固定长度数据类型的规范,并且引入了 64 位数据类型的支持。在 32 位机器上,你可能需要使用 long long 来建立一个 64 位的数据类型。而在 64 位机器上,long 即表示 64 位数据类型。

在 C99 中,引入了新的头文件 在这个头文件中,同一规范了不同长度数据类型的定义:

int8_t, int16_t, int32_t, int64_t 分别代表 8, 16, 32, 64 位的整型 uint8_t, uint16_t, uint32_t, uint64_t 分别代表 8, 16, 32, 64 位的无符号整数 float, double 分别代表了 32, 64 位浮点数

因此,推荐使用引入 #include ,并使用这些固定长度的数据类型,来代替传统的 int, short, long 等。

有时候,如果需要使用原生机器字长的数值类型,以实现最佳性能时,应当使用 intptr_t 类型,它在 32 位机器上等价于 int32_t 而在 64 位机器上等价于 int64_t 。无符号的 uintptr_t 也是如此。

此外,利用 sizeof 返回的类型 size_t 也是这样的。其在不同架构的机器上字长不同。

如果需要确保使用长度最长的数值类型。可以使用类型 intmax_t 和 uintmax_t 作为最大的容器,来确保类型转换时,没有损失和溢出。

2.2 字符类型,宽字节和多字节

传统 C89 标准只支持 ascii 码,而你可能发现 C 语言其已经具有了处理 Unicode 字符集的能力。这最早在 95 年引入,并成为 C99 标准的一部分。

在 C99 中引入了 和 两个头文件,用于处理宽字节。传统的 char 只有 8 位数,因此原生只能容纳所有的 ascii 和 扩展 ascii 字符。而 wchar_t 类型则是 32 位或 16 位,可以容纳所有的 Unicode 字符。但是这只在用于字符统计等需求时,才需要使用到宽字符类型,因此不常见其使用。

而 UTF-8, UTF-16, UTF-32 等字符编码格式都是用不同的编码方式来实现 Unicode 字符集。因此,宽字符类型可以直接容纳 UTF-32 格式的字符,也可以正确的用于统计字数。而 UTF-8 这种通用的字长无关的编码可以直接放在 char 类型的数组中,也可以直接被系统所读取。唯一的问题在于 sizeof 获取的长度并不是真正的字数。

在 C11 中 头文件对字符集的 Unicode 支持进一步扩充。支持定义如下字符串:

char s1[] = "你好"; // 标准支持 char s2[] = u8"你好"; // utf-8 编码 char16_t s3[] = u"你好"; // 16 位宽字符 char32_t s4[] = U"你好"; // 32 位宽字符 wchar_t s5[] = L"你好"; // 根据本机架构决定宽字符长度 2.3 布尔类型

在 C99 规范中引入了新的布尔类型,再也不需要要自行定义了。头文件 包括其实现。布尔类型的关键字是 _Bool,也有一个宏定义为 bool ,取值为 true 和 false 。

因此我们可以这样使用了:

bool found = true; bool empty = false; bool is_foo(); 2.4 复数类型

C99 中引入了复数类型,这意味着我们可以直接表示复数或者平面中的一个点。其声明在 头文件中。分别有三种类型的复数类型:

double complex float complex long double complex

有宏 _Complex_I 或者 I 来声明一个复数。此外还有一些常用的复数函数,例如:

ccos, csin, ccos, csinh 等三角函数和双曲函数

cexp, clog, cabs, cpow, csqrt 等数学函数

carg, cimag, creal 获取象限角、虚数部分、实数部分等函数

下面是一个简单的例子:

double complex a = 1.0 + 2.0 * I; double complex b = 5.0 + 4.0 * I; a *= b; a = csin(b); a = creal(b); 2.5 指针类型

通产需要使用 void* 等来声明一个指针,或者需要使用强制类型转换为 long 来进行运算。在 中定义了专门的指针类型: unitptr_t 和在 终端指针差值类型 ptrdiff_t。

ptrdiff_t diff = (uintptr_t)ptrOld - (uintptr_t)ptrNew; 三、数组和结构体

在 C99 和 C11 中引入了新的特性,可以使我们更加灵活地使用数组和结构体以及联合体。

3.1 可变长数组(VLA)

在 C99 之前,如果数组的长度在编译时无法确定,遇到这种情况,我们通常只有两种做法:一是申请一个足够长度数组(需要对长度进行估计,否则很可能会溢出),一个是使用 malloc 在堆中分配数组(但是需要维护,需要释放等)。

在 C99 之后,引入了可变长数组(VLA)的概念,可以实现数组的长度在编译时不一定需要确定。这样可以实现在运行时确定数组长度,而作用于结束后自动释放。

比如:

int n; int array[n];

但是这种用法也有一些限制,比如:

n 和 array 必须位于同一个文件作用域 不可以用于 typedef 不可使用在结构体中 不可以申明为 static 变量 不可以申明为 extern 变量或 extern 变量的指针 3.2 灵活的初始化

在 C99 中带来了非常灵活的初始化数组和结构体的方法,我们不在需要对完整的数组或者结构体进行初始化,可以只对其一部分进行初始化。比如:

uint32_t a1[64] = {0}; // 全部填充 0 struct thing { uint64_t index; uint32_t counter; }; struct thing t1 = {0}; // 填充 0 uint32_t a2[10] = {[2] = 1, [4] = 6}; //对数组部分位置赋值。 struct thing t2 = {.index = 3} // 结构体部分位置赋值 struct thing t3 = {counter: 0}; // 也可以使用类似 Python 的形式 3.3 alignof

在 C11 标准中,定义了新的 alignof 运算符,和 sizeof 相对应。在头文件 中申明。定义了一个对象的对齐要求。

alignof(char); // 1 alignof(struct {char c; int n;}; // 4 alignof(float[1024]); // 4 四、宏定义和预编译

C99 在宏定义部分有一些新的变化,最常用的就是 Pragma 运算符和可变宏的引入。

4.1 Pragma 运算符

C99 中引入。主要有 _Pragma 运算符和 #pragma 宏。是用于指定编译时的行为,比如:

# 编译时显示消息 #pragma message(“_X86 macro activated!”) # 注释 #pragma comment(…)

此外,#pragma once 使用的非常多,这是一个非标准但是被普遍实现的特性(Clang, GCC, Visual C 等主流编译器均支持)。用于指出该头文件只引入一次。和下面语句等效:

#ifndef xxx #def xxx #endif 4.2可变长宏

定义宏的时候可以引入不定长度的输入参数,具体用法不在列出。

五、兼容 C++ 的改变

这里是一些引入的 C++ 中的特性。

5.1 单行注释

在 C99 中引入了单行注释 // 这个在 C++ 中早已实现,也被较多编译器所支持。在此被列入了标准。

5.2 任意位置申明

早前的 C 语言申明语句一定位于语句块的最开头。而 C99 之后打破了这种约定,可以在任意位置申明语句。因此下面的内联计数器也可以直接使用:

for(int i = 0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有