C语言

C语言（英语：C Language）是一种通用的、过程式编程程式语言，支持结构化编程、词法作用域和递归，使用静态类型系统，并且广泛用于系统软件与应用软件的开发。

C语言于1969年至1973年间，为了移植与开发UNIX操作系统，由丹尼斯·里奇与肯·汤普逊，以B语言为基础，在贝尔实验室设计、开发出来。二十世纪八十年代，C语言应用日渐广泛。为了避免各开发厂商用的C语言的语法产生差异，美国国家标准局为C语言订定了一套完整的国际标准语法，称为ANSI C，作为C语言的标准。与此同时，国际标准化组织也接受该标准为国际标准。因此，ANSI C也同时被称为ISO C。二十世纪八十年代至今的有关程式开发工具，一般都支持符合ANSI C的语法。

C语言具有高效、灵活、功能丰富、表达力强和较高的可移植性等特点，在程式设计中备受青睐，成为最近25年使用最为广泛的编程语言。目前，C语言编译器普遍存在于各种不同的操作系统中，例如Microsoft Windows、macOS、Linux、Unix等。C语言的设计影响了众多后来的程式语言，例如C++、Objective-C、Java、C#等。现行的许多软件都是由C语言或者其影响和衍生的编程语言开发出来的。

## 概述

与ALGOL一族的大多数过程式编程语言类似，C语言是一个有结构化程式设计、具有变量作用域（variable scope）以及递回功能的程序式语言。其采用的静态类型系统可以防止无意的程序设计操作。C语言中所有的可执行代码都被包含在子程序（函数）里。其传递参数均是以值传递（pass by value），另外也可以传递指针（a pointer passed by value）。C语言是自由形式语言，即其源代码的缩进并不影响程序的功能，而是使用分号作为语句的结尾，花括号来表示代码块。

由于C语言的语言规模较小，若干高层的机制需要使用定义的函数来提供。比如，C语言并没有直接处理复合对象（例如字符串、集合、列表、数组等）的操作，也没有对于存储器分配工具和内存回收工具的直接定义，同时也本身不具有输入和输出以及文件访问的方法。然而，用户定义的函数和C语言标准库中的函数为这些高层的机制提供了可能性。

C语言也具有以下的特性：

C语言提供字元、整数、浮点数等基本资料型别，并支援指标、阵列、结构体与联合体等衍生资料型别。其型别系统允许部分资料型别之间进行转换，例如整数与字元型别之间的转换。

作为一种接近硬件层的程式语言，C语言透过指标机制提供对内存的直接操作能力，使程式设计者能够进行较低阶的系统控制。同时，不同型别的资料也可以透过结构体组合为更复杂的资料结构。

在程控方面，C语言提供条件判断、多路选择、循环等基本控制结构，并以函式作为程式组织与重复利用的核心机制。函式可以回传不同型别的资料，也支援递回呼叫，每次呼叫时都会建立独立的区域变数环境。

此外，C语言的保留字数量相对较少，使变数与函式命名具有较大的弹性。其前置处理器（preprocessor）则提供宏展开、条件编译与档案引用等功能，进一步增强程式开发与编译过程的灵活性。

## 历史

20世纪70年代，肯·汤姆森为了使其设计的Unix系统更加高效，使用B语言的变种（即C语言）在DEC PDP-7计算机上重写了Unix。C语言中许多重要概念来源于BCPL语言，其对C语言的影响也间接地来源于B语言。在1978年，丹尼斯·里奇和布莱恩·柯林汉合作出版了《C程序设计语言》第一版，事实上即为K&R C标准。1983年，为了制定一个独立于具体机器且无歧义的C语言标准，美国国家标准协会成立了一个委员会，并在1988年完成了该标准的制定，即ANSI C。此标准同时被国际标准化组织所采纳，也被称作ISO C。

其后，C语言至今经历了几次标准更新，诞生了C99、C11、C18和目前最新的标准C23。C语言标准的下一次更新C2Y目前正在起草中。

## 语法

C语言的语法相对简洁而直接。C语言的形式文法由国际标准化组织所制定。简单来说，C语言包括如下文法：

- 作为一种指令式编程语言，C语言使用语句执行操作。最常见的语句是表达式语句，由一个表达式后加一个分号组成，可以令系统调用函数和为变量赋值。
- 注释： C语言支持单行注释（以`//`开头）和多行注释（以`/*`开始，以`*/`结束）。
- 数据类型：基本的数据类型包括整数（`int`和`long long`）、浮点数（`float`、`double`和`long double`）、字符（`char`）、枚举`enum`等。
- 数组：数组是一组相同类型的数据元素的集合。使用以下方法初始化一个五个元素的整数数组：

```c
int numbers[5] = {1, 2, 3, 4, 5};
```

- 封装结构：结构（`struct`）、联合（`union`）。
- 结构化编程和控制结构： C语言包括条件语句（`if`、`else`、`switch-case`）、循环语句（`for`、`while`、`do-while`）等。
- 跳转语句：C语言允许使用跳转关键字`goto`、`break`和`continue`来实现程序块之间的跳转，这和汇编语言的`jmp`关键字有一定相似处。
- 函数： C语言中的函数是程序的基本模块，可以自定义函数并在程序中调用。
- 灵活且靠近底层的内存控制机制：C程序员可以自由选择分配何种内存，以及分配多大的内存，如下代码所示：

```c
#include <stdlib.h>     //调用分配内存需要的函数
int *array = (int *)malloc(5 * sizeof(int)); // 分配一個包含五個整數的數組
free(array); // 釋放使用malloc分配的內存
```

## Hello World 程序

<image>

"对于所有语言的初学者来说，编写的第一个程序几乎都是相同的，即‘请打印出下列内容 hello, world’"

现在广泛被编程初学者使用的"hello, world"程序实例最初就是出现在《C程序设计语言》第一版中。下面是一个在标准输出设备（stdout）上打印出 "Hello, world!" 字串的简单程式。类似的程式，通常作为初学程式语言时的第一个程式：

```c
#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}
```

其中只有int，void，return为C语言的关键字，预处理器会将`#include <stdio.h>`替换为stdio.h文件的内容。

main函数是C语言程序的入口点。

`"Hello, world!\n"`中的`\n`是一个转义字符，形式为`\`加上一个字符。所起的作用在ASCII码中规定。

`printf`是声明于stdio.h的函数，其参数为格式化字符串。

`return 0;`代表程式返回代表正常退出的退出状态，现在常见的编译器会自动补上。

关于main函数的定义，`void main()` 的用法并不是任何标准制定的。C语言标准语法是 `int main()`，任何实现都必须支持`int main(void) { /* ... */ }`和`int main(int argc, char* argv[]) { /* ... */ }`。在 C++ 标准中，main的标准型态应是int，否则类型是由实现定义的。任何实现都必须支持`int main() { /* ... */ }`和`int main(int argc, char* argv[]) { /* ... */ }`。

## 内存管理

C语言的特色之一是：程序员必须亲自处理内存的分配细节。语言不负责内存边界检查，这是因为在运行时进行内存边界检查会造成性能问题，与UNIX哲学不符。此特性容易导致缓冲区溢出问题。然而，部分编译器（如英特尔编译器）会出于安全性的考量，提供方法以进行运行时内存边界检查。

大多数C语言实现使用栈（Stack）来保存函数返回地址／栈帧基址、完成函数的参数传递和函数局部变量的存储。然而，在部分极特殊的平台上，使用栈并不能获得最大效率。此时的实现由编译器决定。

如果程序需要在运行的过程中动态分配内存，可以利用堆（Heap）来实现。

基本上C程序的元素存储在内存的时候有3种分配策略：

- 静态分配

如果一个变量声明为全局变量或者是函数的静态变量，这个变量的存储将使用静态分配方式。静态分配的内存一般会被编译器放在数据段或代码段来存储，具体取决于实现。这样做的前提是，在编译时就必须确定变量的大小。

以IA32的x86平台及gcc编译器为例，全局及静态变量放在数据段的低端；全局及静态常量放在代码段的高端。

- 自动分配

函数的自动局部变量应该随着函数的返回会自动释放（失效），这个要求在一般的体系中都是利用栈（Stack）来满足的。相比于静态分配，这时候，就不必绝对要求这个变量在编译时就必须确定变量的大小，运行时才决定也不迟，但是C89仍然要求在编译时就要确定，而C99放松了这个限制。但无论是C89还是C99，都不允许一个已经分配的自动变量运行时改变大小。

所以说C函数永远不应该返回一个局部变量的地址。

要指出的是，自动分配也属于动态分配，甚至可以用alloca函数来像分配堆（Heap）一样进行分配，而且释放是自动的。

- 动态分配

还有一种更加特殊的情况，变量的大小在运行时有可能改变，或者虽然单个变量大小不变，变量的数目却有很大弹性，不能静态分配或者自动分配，这时候可以使用堆（Heap）来满足要求。ANSI C定义的堆操作函数是malloc、calloc、realloc和free。

使用堆（Heap）内存将带来额外的开销和风险。

## 库

C语言的标准文档要求了一个平台移植C语言的时候至少要实现的一些功能和封装的集合，称为“标准库”，标准库的声明头部通过预处理器命令#include进行引用。

在C89标准中：

文件｜简介说明

断言相关

字符类型判断

标准报错机制

浮点运算

各种体系结构限制

本地化接口

数学函数

跨函数跳转

信号（类似UNIX的信号定义，但是差很远）

可变参处理

一些标准宏定义

标准I/O库

标准工具库函数

ASCII字符串及任意内存处理函数

`<time.h>`｜时间相关

在94年的修正版中

在C99中增加了六个函式库

以上是C语言的标准。各个系统各自又对C库函数进行的各种扩充，就浩如烟海了。如POSIX C、GNU C等。

## 工具软件

工具软件可以帮助程式设计者避免一些程式中潜藏或容易出现的问题，例如常会造成程式未预期动作或是执行期错误的程式码。

许多语言都有自动源代码检查及审计工具，C语言也有类似工具，像是Lint。可以在程式刚写好时用Lint找出可能有问题的程式，通过Lint后再用C编译器进行编译，许多编译器也可以设定是否要针对一些可能有问题的程式码提出警告。MISRA C是一套针对嵌入式系统的法则，可主要也是避免一些可能有问题的程式码。

也有一些编译器、程式库或操作系统可以处理一些非标准C语言的功能，例如边界值检查、缓存溢出侦测、序列化及自动垃圾回收功能。

使用像Valgrind或IBM Rational Purify等软件工具，或者连结有特别malloc函式的程式库，有助于找出一些运行期内存使用的问题。

## 外部链接

- Coding Programmer Page / C Library Reference and Examples (english)
- GCC 首页
- GLIBC2 首页
- Visual Studio Express 首页
- ISO/IEC 9899。C99标准的官方网站。目前（2020年）可直接下载的标准文本是 ISO/IEC 9899:202x。

## 基本资料

- 日语原文：C语言
- 域：在第一部介绍C语言的书籍《C程序设计语言》的初版封面上使用的C标识
- 编程范型：程序式指令式编程（过程式）、结构化编程
- 发行时间：1972年
- 设计者：丹尼斯·里奇（Dennis Ritchie）
- 开发者：丹尼斯·里奇（Dennis Ritchie）和肯·汤普逊（Ken Thompson）
- 软件版本周期： ISO/IEC 9899:2018（C18）
- 软件版本周期：2018年6月
- 类型系统：静态, 弱类型, 明示类型, 名称类型系统
- 软件实现：Clang、GCC、MSVC、Turbo C、Watcom C
- 受影响于：B（BCPL、CPL）、ALGOL 68、组合语言、PL/I、FORTRAN
- 施影响于：、如：awk、BitC、csh、C++、C#、D、Java、JavaScript、Objective-C、Perl、PHP、Rust等
- 操作系统：跨平台
- wikibooks：C Programming