C 语言发展历史|标准
1972年,丹尼斯·里奇(Dennis Ritch)和肯·汤普逊(Ken Tompson)在贝尔实验室开发 UNIX 操作系统时基于 B 语言设计出 C 语言。
1987年,布莱恩·柯林汉(Brian Kernighan)和丹尼斯·里奇合著的 The C Programming Language)(《C 语言程序设计》)第 1 版是公认的 C 标准,称之为 K&R C
或经典 C。
1989年,C 语言被美国国家标准协会 ANSI 标准化为 ANSI C
,其定义了 C 语言和 C 标准库。这套 C 标准也被称为 C89
。
1990年,国际标准化组织 ISO 采用了 ANSI 制定的 C 标准,改为 ISO C
。它与 ANSI C
是完全相同的。ISO C
也称为 C90
。
1994年,ANSI/ISO联合委员会发布 C99
标准。其修改如下:
- 增加 3 个新的标准库头文件:
iso646.h
、wctype.h
和wchar.h
。 - 几个新的记号与预定义宏,用于对国际化提供更好的支持。
printf/sprintf
函数一系列的格式代码。- 大量的函数与一些类型和常量,用于多字节字符和宽字节字符。
2011年,ISO 发布了 C11
标准。
C18
C2X
C 语言的特性
- 可移植性(Portability):C语言的代码在不同平台上通常可以轻松移植,这意味着您可以在不同的计算机体系结构和操作系统上运行相同的代码,只需稍作修改。这得益于C语言的抽象性和硬件无关性。
- 高性能(Performance):C语言具有卓越的性能,因为它允许程序员对计算机硬件进行更低级别的控制。这使得C语言非常适合开发需要高性能的应用程序,如系统软件、嵌入式系统和游戏引擎。
- 低级别编程(Low-Level Programming):C语言允许程序员直接操作内存和硬件资源,包括指针操作、位操作和汇编语言嵌入。这使得C语言非常适合系统编程和硬件控制。
- 可扩展性(Extensibility):C语言允许程序员编写自己的函数和库,以扩展语言的功能。这使得C语言可以应对各种不同的问题领域。
- 面向过程编程(Procedural Programming):C语言是一种面向过程的编程语言,它以函数为基本构建块,使得程序的组织和管理变得简单。
C 语言的应用领域
- 系统编程:C 语言最初设计用于 Unix 系统的开发,因此在系统内核、驱动程序和系统工具的编写中得到广泛应用。它允许程序员直接访问计算机硬件,提供了对系统资源的更好控制。
- 嵌入式系统:C 语言的低级别控制和高效性使其成为嵌入式编程的理想选择,包括微控制器、嵌入式系统-on-chip(SOC)和嵌入式软件。
- 编译器和解释器:C 语言也用于编写其他编程语言的编译器和解释器。例如 Python 的 CPython 解释器就是用 C 语言写的。
- 游戏开发:虽然游戏的高级逻辑通常由 C++ 等语言编写,但是 C 语言在游戏引擎、图形编程和性能优化等方面仍起到关键作用。许多游戏的底层引擎和系统组件都是用C语言编写的。
- 数据库系统:关系型数据库管理系统(RDBMS)如 MySQL 和 PostgreSQL 使用 C 语言进行核心开发。C 语言的性能和可移植性对数据库系统至关重要。
- 网络编程:C语言广泛用于开发网络应用程序、服务器和协议栈。它提供了对底层网络套接字编程的支持,使得开发网络应用更加灵活。
- 图形编程:C语言用于开发图形应用程序、图形库和图形用户界面(GUI)工具包。例如,GTK+和Qt这些GUI库都支持C语言。
- 科学和工程计算:C语言在科学计算、数值分析和工程领域得到广泛应用,因为它的性能非常高。许多数学库和科学计算工具都使用C语言编写。
- 金融领域:C语言在金融领域用于高性能的金融建模、算法交易和数据分析应用程序的开发。其速度和精度对于金融计算至关重要。
编译与链接
C 语言编写的源代码文件以 .c
结尾。
编译器(compiler)将源代码编译成中间代码或目标代码,最普遍的中间代码是机器语言代码,但是中间代码还不能直接运行,因为缺少启动代码和库代码。启动代码是程序与操作系统间的接口。不同操作系统的启动代码不同。
链接器最后把中间代码、系统的标准启动代码和库代码这三部分合并成一个文件:可执行文件。中间代码与可执行代码都由机器语言指令组成。
C 语言的关键字与保留标识符
K&R C标准关键字:
auto | extern | short | while |
break | float | case | for |
char | goto | static | if |
struct | continue | switch | default |
int | typedef | do | long |
union | double | register | unsigned |
else | restrict | void | return |
C89/C90 标准关键字:
- signed
- const
- enum
- volatile
C99 标准关键字:
- inline
C11 标准关键字:
_Alignas
_Alignof
_Atomic
_Bool
_Complex
_Generic
_Imaginary
_Noreturn
_Static_assert
_Thread_local
保留标识符包括那些以下划线字符开头的标识符和标准库函数名,如printf()
、scanf()
、malloc()
等。
C 语言基本数据类型
整数类型
int
int 类型是有符号整型,可以是正负整数和零,第一位为符号位。
int 类型的取值范围与计算机处理器位数相关:16位的处理器使用16位来存储一个 int 值,取值范围为 − 2 15 = 32768 到 2 15 − 1 = 32767 -2^{15} = 32768 到 2^{15} -1 = 32767 −215=32768到215−1=32767,这也是 ISO 规定的 int 最小的取值范围;32位处理器则使用32位来存储一个 int 值;还有 64 位的。
声明 int 变量:
C语言中,在数字前添加 0x 或 0X
前缀表示 16 进制,添加 0
前缀表示 8 进制。
使用 printf
库函数在控制台打印 int 变量:
%d
—转为—> 十进制整数%o
—转为—> 八进制整数%x
—转为—> 十六进制整数%#o
—显示—> 八进制整数%#x
—显示—>0x
开头十六进制整数%#X
—显示—>0X
开头十六进制整数
不止上面这些哦!
short
short 是 short int
类型的简写,其占用存储空间不比 int 多,可能相等,一般为 16 位。在某些情况下可以节省空间。
short 的打印可以使用 %hd
(十进制)、%ho
(八进制)和 %hx
(十六进制)。
long
long 是 long int
的简写,占用空间不比 int 少,一般为 32 位。适用于数值大的场合。
long 的打印可以使用 %ld
(十进制)、%lo
(八进制)和 %lx
(十六进制)。
对于 long 类型常量,后缀要加 l
或 L
。
long long
long long int
的简写,占用空间不比 long 少,至少占用 64 位。
long long 的打印可以使用 %lld
(十进制)、%llo
(八进制)和 %llx
(十六进制)。
long long 类型常量后缀要加 ll
或 LL
。
unsigned
unsigned int
的简写,表示无符号整型。
C90 中添加了 unsigned long
或 unsigned long int
,以及 unsigned short
和 unsigned short int
。
C99 中添加了 unsigned long long
或 unsigned long long int
。
有符号类型前面都省略了 signed
。加上也无妨。
常量后缀:
类型 | 后缀 |
---|---|
unsigned | %u |
unsigned long | %lu、%ul、%UL、%LU … |
unsigned long long | %llu … |
...
后面显而易见了。
字符类型:char
在C语言中,字符类型主要用于表示单个字符。C语言提供了两种主要的字符类型:char
和 wchar_t
。
char
变量通常占用一个字节的内存空间,即8位,但其确切大小和编码方式可能因不同的编译器和平台而异。
char
类型可以用来表示标准ASCII字符,包括字母、数字、标点符号和一些控制字符。
在C语言中,char
类型的变量可以用单引号括起来,例如:char ch = 'A';
。
wchar_t
是C语言中的宽字符类型,用于表示更广范围的字符,包括国际化字符集中的字符。
wchar_t
变量的大小和编码方式因平台而异,通常会比 char
大。
在C语言中,wchar_t
类型的变量可以用L
前缀和单引号括起来,例如:wchar_t wideChar = L'宽';
。
_Bool 类型
_Bool
是C语言中的一个基本数据类型,用于表示布尔值,即"真"或"假"。C99标准引入了这个数据类型,以提供对布尔逻辑的原生支持。
_Bool
类型只有两个合法的值,即 0
表示 “假”,非零值表示 “真”。通常,“假” 用 0
表示,而 “真” 可以是任何非零值,但C标准要求 _Bool
类型的实现至少能够表示 0
和 1
,因此通常用 0
表示 “假”,用 1
表示 “真”。
要使用 _Bool
类型,需要包含 <stdbool.h>
头文件。
为了更方便地使用 _Bool
,C标准库还定义了 bool
作为 _Bool
的别名。
可移植类型:stdint.h 和 inttypes.h
为了提高可移植性,C99 新增了两个头文件 stdint.h
和 inttypes.h
。
stdint.h
提供精确宽度整数类型(exact-width integer type)。例如 int32_t
表示 32 位的有符号整数,在 32 位 int 的系统中会将其作为 int 别名,而在 16 位 int、32 位 long 的系统中则会将其作为 long 的别名。但是,如果系统中并没有 32 位这么短的类型,那么这时就有问题了。
因此,C99和 C11提供了最小宽度类型(minimum width type)来解决该情况。例如,int_least8_t
是可容纳 8 位有符号整数值的类型中宽度最小的类型的一个别名。如果系统最小数据类型是 16 位,那么int_least8_t
可能被实现为 16 位。
如果你更关心数据类型的速度而非空间,那么 C99 和 C11 也定义一组可使计算达到最快的类型集合,称为最快最小宽度类型。例如 int_fast8_t
。
最后,还有最大整数类型 intmax_t
和 uintmax_t
。
这些类型都被包含在 stdint.h
中。
inttypes.h
定义了一系列的宏,用于在格式化字符串中指定不同大小和带符号性质的整数类型。这些宏通常以PRI
和SCN
开头,后面跟随整数类型的缩写,例如 PRId32
用于表示带符号32位整数的格式宏,PRIu64
用于表示无符号64位整数的格式宏。这些宏可用于 printf
和 scanf
等函数,以确保在不同平台上正确处理整数类型的输入和输出。
printf("%"PRId32"",x);
浮点类型:float、double和long double
浮点类型用于金融和数学领域的场合。
一般计数法:120.55
科学计数法:1.2055 x 10^2
指数计数法:1.2055e2
float 类型至少表示小数点后 6 位有效数字,取值范围为 1 0 − 37 至 1 0 37 10^{-37} 至 10^{37} 10−37至1037。
float 类型通常占据 32 位,采用 IEEE 754 标准来表示浮点数,其中 8 位用于表示指数的符号和值,另外 24 位用于表示尾数及其符号。
IEEE 754 是一个用于浮点数表示和计算的标准,它定义了浮点数的二进制表示方法、舍入规则以及基本算术运算规则。
让我们考虑一个单精度浮点数:0 10000010 10010000000000000000000。
IEEE 754 单精度浮点数由三个部分组成:符号位(Sign Bit)、指数部分(Exponent)、小数部分(Fraction/Mantissa)。让我们一步步来解剖这个例子:
- 符号位(Sign Bit):第一个位是符号位。0 表示正数,1 表示负数。在这个例子中,符号位是0,所以这是一个正数。
- 指数部分(Exponent):接下来的 8 位是指数部分。这个部分用于表示浮点数的指数。在 IEEE 754 中,指数采用偏移表示法,即需要减去一个偏移值来得到真实的指数。偏移值为127(单精度浮点数的情况)。所以,在这个例子中,指数部分是 10000010。要计算真实的指数,需要将它解释为二进制,然后减去偏移值:
10000010 (二进制) = 130 (十进制) 真实的指数 = 130 - 127 = 3 - 小数部分(Fraction/Mantissa):剩下的位数(23 位)组成小数部分。这个部分用于表示浮点数的精度和小数部分。在这个例子中,小数部分是 10010000000000000000000。
现在,我们有了这些部分的值,我们可以计算出这个浮点数的实际值。根据 IEEE 754 的规则,实际值可以计算为:
实际值 = (-1)^符号位 × 1.10010000000000000000000(二进制) × 2^真实的指数
在这个例子中:
- 符号位是 0,表示正数。
- 小数部分是 1.10010000000000000000000(二进制)。
- 真实的指数是 3。
现在,我们可以将这些值代入公式中来计算实际值:
实际值 = (-1)^0 × 1.10010000000000000000000 × 2^3
实际值 = 12.5(十进制)
double 是双精度浮点型,与 float 最小取值范围相同,但是最小有效数字为 10 位,double 一般占用 64 位。
long double 至少与 double 精度相同。