c语言

通用的过程式编程语言

c语言

C语言是一种通用、面向过程的编程语言,具有高效、简洁、灵活等特点,被广泛应用于各个领域的软件开发和系统编程[5]。具体来说,它是一种命令式过程语言[6],支持结构化编程、词法变量作用域和递归,具有静态类型系统[7]。从设计上讲,C语言的设计目标是提供足够高级的抽象和结构,接近底层硬件的控制。这使得C语言非常适合于系统级编程和底层开发,能够直接访问硬件资源,并具有高效的性能[8]。C语言常用于从最大的超级计算机到最小的微控制器和嵌入式系统等各种计算机架构[9][10]。C语言是编程B语言的后继语言,最初由丹尼斯·里奇于1972年至1973年在贝尔实验室开发,用于构建在Unix上运行的实用程序[1]。1978年 ,由C语言原设计者合著的《C程序设计语言(The C Programming Language)》一书问世,随后C语言开始广泛使用,该书也是C语言的首要参考语言规范[11][12]。在20世纪80年代,C语言逐渐流行起来,并由美国国家标准学会(ANSI C)和国际标准化组织(ISO)主要负责对C语言的标准化[13]。时至21世纪,它已成为使用最广泛的编程语言之一[14],几乎所有现代计算机架构和操作系统都有C编译器可用。

C语言是一种通用、面向过程的编程语言,具有高效、简洁、灵活等特点,被广泛应用于各个领域的软件开发和系统编程[6]。具体来说,它是一种命令式过程语言[7],支持结构化编程、词法变量作用域和递归,具有静态类型系统[8]。从设计上讲,C语言的设计目标是提供足够高级的抽象和结构,接近底层硬件的控制。这使得C语言非常适合于系统级编程和底层开发,能够直接访问硬件资源,并具有高效的性能[9]。C语言常用于从最大的超级计算机到最小的微控制器和嵌入式系统等各种计算机架构[10][11]

C语言是编程B语言的后继语言,最初由丹尼斯·里奇于1972年至1973年在贝尔实验室开发,用于构建在Unix上运行的实用程序[2]。1978年 ,由C语言原设计者合著的《C程序设计语言(The C Programming Language)》一书问世,随后C语言开始广泛使用,该书也是C语言的首要参考语言规范[12][13]在20世纪80年代,C语言逐渐流行起来,并由美国国家标准学会(ANSI C)和国际标准化组织(ISO)主要负责对C语言的标准化[14]。时至21世纪,它已成为使用最广泛的编程语言之一[15],几乎所有现代计算机架构和操作系统都有C编译器可用。

一个遵循标准的C程序,有高可移植性,仅需少量修改,就可在多种计算机平台和操作系统上进行编译。此外,C语言具有简单的语法和丰富的库函数,为程序员提供了广泛的功能和灵活的编程方式[8]

自1988年以来,C语言在衡量编程语言受欢迎程度的TIOBE指数排名中保持在前两位[15]

概述

C语言是一种通用的、面向过程式的计算机编程语言,与大多数ALGOL族(ALGOrithmic Language,指令式编程语言族)的大多数过程式编程语言类似[2]。和C++C#Java等面向对象编程语言有所不同的是,C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、仅产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言[8]。C语言描述问题比汇编语言迅速、工作量小、可读性好、易于调试、修改和移植,一般只比汇编语言代码生成的目标程序效率低10%-20%,而代码质量与汇编语言相当。因此,C语言十分适用于编写系统级软件[16]

从设计细节中来说,C语言采用静态类型系统和有结构化程序设计的特性,具有变量作用域和递归功能。C语言中的可执行代码包含在子程序(函数)中,其函数参数大多采用值传递方式,而数组等特殊数据结构则通过传递指针(指向数组第一个元素的地址)来传递[17]

1999年ISO关于C语言概念
1999年ISO关于C语言概念

C语言还具有以下的特性:

1.基本数据类型包括字符、整型和浮点数等,还有指针、数组、结构等派生数据类型、void类型和枚举类型。

2.变量类型可以转换,如整数型和字符型变量。

3.可以通过指针对存储器进行低级控制。

4.不同的变量类型可以组合在一起形成结构体。

5.支持基本的控制流,如条件判断、循环等。

6.函数可以返回各种数据类型的值,并且可以递归调用。

7.C语言目前有44个保留字,允许灵活的变量和函数命名。

8.编译预处理机制增强了C语言的灵活性[17]

C语言的起源和创造者

C语言最早由丹尼斯·里奇(Dennis Ritchie)为了在PDP-11电脑上运行的Unix系统所设计出来的编程语言,初次发展在1969年到1973年之间。其起源也与Unix操作系统的开发密切相关。它源于BCPL语言,后者由马丁·理察德(Martin Richards)于1967年左右设计实现[2]

1969年,PDP-7是贝尔实验室内可用的计算机之一,它是一台由DEC(Digital Equipment Corporation)生产的小型计算机[19]。在项目初期,实验室人员选择一台已经可用的计算机进行开发可能会更加方便,而不需要额外的投资和配置。当时,PDP-7上运行的操作系统很有限,而实验需要更强大的操作系统。所以肯·汤普逊(Ken Thompson)和里奇等人在贝尔实验室开始了一个项目,目标是为PDP-7开发一个更强大和通用的操作系统。这个项目的成果就是UNICS(Uniplexed Information and Computing Service),它奠定了后来UNIX操作系统的基础,为计算机领域带来了重大影响,也被称作第一版UNIX[20]

PDP-7
PDP-7

开发操作系统后,肯希望有一种编程语言来为该平台开发实用程序。1970年,为运行在PDP-7上的首个Unix系统创建了一个最新开发的BCPL系统编程语言的精简版本[22],被称为B语言[9]。因为PDP-7的性能不佳,肯·汤普逊与丹尼斯·里奇决定把第一版UNIX移植到PDP-11/20的机器上,开发第二版UNIX。但是由于B语言的速度太慢,而且无法利用PDP-11的字节寻址能力等特性,最终用B语言编写的实用程序寥寥无几[23]

肯·汤普森(坐着)和丹尼斯·里奇一起用PDP-11工作
肯·汤普森(坐着)和丹尼斯·里奇一起用PDP-11工作

PDP-11出世后,贝尔实验室也升级了设备,用PDP-11代替了PDP-7。为了利用功能更强大的PDP-11的特性,1971年丹尼斯开始改进B语言。一个新增的重要功能是字符数据类型,他称之为新B语言(NB)。与此同时,肯开始使用NB语言编写Unix内核,并决定了NB语言的发展方向,有了int和char数组[25]。此外,还增加了指针、生成指向其他类型的指针的能力、所有类型的数组以及从函数返回的类型。表达式中的数组变成了指针,并编写了新的编译器,语言也更名为C语言[2]。自此,C语言正式问世,C语言编译器和一些实用程序也被收录到第二版Unix(也被称为Research Unix)中[23]

1973年左右,C语言预处理器被引入第四版Unix[2],这是C语言第一次应用在操作系统的核心编写上。此时,C语言已经具备了一些强大的功能,如结构类型(struct)。预处理器的最初版本只提供了包含文件和简单的字符串替换:无参数宏的#include和#define。此后不久,迈克·莱斯克(Mike Lesk)和约翰·雷泽(John Reiser)等人对其进行了扩展开发,纳入了带参数和条件编译的宏[2]。1975年起,C语言开始移植到其他机器上使用,从那时起,C在大多数计算机上被使用,从最小的微型计算机到与CRAY-2超级计算机。当时的C语言很规范,即使没有一份正式的标准,人们也可以写出C程序,并且无须修改就可以运行在任何支持C语言和最小运行时环境的计算机上[25]

发展历程

年份非正式名称C标准发展变化
1978K&R C引入了多种语言特性,包括[27]
1.标准I/O库
2.long int数据类型
3.无符号int数据类型
4.将=op形式的复合赋值运算符改为op=形式,消除了语义歧义[28]
在1989年ANSI标准发布之后,K&R C仍然被认为是C程序员考虑实现最大可移植性时所采用的基础规则[28]
1989/1990ANSI C, ISO C, 标准C,C89, C90[9]ISO/IEC 9899:1990改进主要包括:
1.增加了真正的标准库
2.新的预处理命令与特性
3.函数原型允许在函数申明中指定参数类型
4.一些新的关键字,包括const、volatile与signed
5.宽字符、宽字符串与多字节字符
6.对约定规则、声明和类型检查的许多小改动与澄清[29]
1999C99ISO/IEC 9899:1999引入了一些新特性:
1.内联函数
2.几种新的数据类型(包括long long int和表示复数的复数类型)
3.可变长度数组和灵活的数组成员
4.对IEEE 754浮点数的支持改进
5.支持可变参数宏
6. 以“//”开头的一行注释
改变了一些规定:
1.没有类型说明符的声明不再隐式地假定为int
2.标准宏“__STDC_VERSION__”被定义为值“199901L”,表示支持C99
3.要求以转义字符的形式(例如\u0040或\U0001f431)支持Unicode标识符,并建议支持原始Unicode名称[30]
2011C11,C1xISO/IEC 9899:2011添加了许多新特性:
1.泛型
2.匿名结构体
3.改进的Unicode支持
4.原子操作、多线程和带边界检查的函数等
它还将现有C99库的部分内容变为可选,并提高了与C++兼容性。标准宏“__STDC_VERSION__”被定义为“201112L”,表示支持C11[31]
2018C17, C18ISO/IEC 9899:2018进行了技术修正,对C11中的缺陷进行了补充。标准宏“__STDC_VERSION__”被定义为“201710L”[32]
下一版(宣布发布于2024)[26]C23,C2xISO/IEC 9899:2023-

字符集

基本的C源字符集包括以下字符[31]

ISO基本的小写和大写拉丁字母:a-z、A-Z

十进制数字:0-9图形字符:! " # % & ' ( ) * + , - . / : ; < = > ? [ \ ] ^ _ { | } ~空白字符:空格、水平制表符、垂直制表符、换页、换行

其中,换行表示文本行的结束,它不一定要对应于一个实际的单个字符,但是为了方便起见,C将其视为一个字符。多字节编码字符可以在字符串文字中使用,但它们不是完全可移植的。基本的C执行字符集包含相同的字符,以及警报、退格和回车等表示,每版C标准修订版都增加了关于扩展字符集的支持[31]

关键字

截至C11,有44个关键字,也称为保留字,这些关键字是预定义的,除了它们预定用途之外,不能被用于的任何其他目的[33]

autodoubleintstruct
breakelselongswitch
caseenumregistertypedef
charexternreturnunion
constfloatshortunsigned
continueforsignedvoid
defaultgotosizeofvolatile
doifstaticwhile
_Bool_Complex_Imaginaryinline
restrict_Alignas_Alignof_Atomic
_Generic_Noreturn_Static_assert_Thread_local

C23将添加14个关键字,并将以前定义一些关键字成为备选关键字,包括:_Alignas、_Alignof、_Bool、_Static_assert、_Thread_local。

alignas[34]alignof[34]bool[34]constexpr[35]
false[36]nullptr_t[37]static_assert[34]thread_local[34]
true[36]typeof[38]typeof_unqual[38]_Decimal128[39][40]
_Decimal32[39][40]_Decimal64[39][40]--

操作符

C语言支持丰富的运算符集,这些运算符是在表达式中使用的符号,用于指定在计算该表达式时要执行的操作[31]

操作符号
算术符号+、-、*、/、%
赋值符号=
增强赋值符号+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=
位逻辑~、&、|、^
位移<<、>>
布尔逻辑!、&&、||
条件评估?:
相等性测试==、!=
调用函数( )
增加和减少++、--
成员选择.、->
对象大小sizeof
类型typeof、typeof_unqual(自C23起)
顺序关系<、<=、>、>=
引用和解引用&、*、[]
顺序
子表达式分组( )
类型转换(类型名)

变量和数据类型

C语言是一种静态类型的编程语言,它要求在使用变量之前必须先声明变量的数据类型。变量是用于存储和操作数据的一种基本概念,而数据类型则决定了变量可以存储的数据的种类和范围。数据类型构成了数据元素存储的语义和特征,它们在语言语法中以内存位置或变量声明的形式表达。数据类型还决定了数据元素的操作类型或处理方法[31]。为便于理解,可以将数据类型粗略的分为基本数据类型,枚举类型,void类型、枚举类型和派生类型[41][42],这些类型基本可以涵盖C语言常用数据类型。但在ISO标准里,还可以有其他归类方式,如算术类型和指针类型统称为标量类型;数组和结构类型统称为聚合类型;数组、函数和指针类型统称为派生声明器类型等等[42]

算数类型

C语言提供了int、float和double三种基本数据算数类型(arithmetic types)说明符,以及有符号、无符号、短和长等修饰符。下表列出了在指定特定存储大小声明时允许的组合[18]

关键字说明最小大小(位)格式化字符串范围
short short int、
signed short、
signed short int
短符号整数类型,至少包含 [-32,767, +32,767][18]16%hi 或 %hdSHRT_MIN - SHRT_MAX
unsigned short、
unsigned short int
短无符号整数类型,至少包含 [0, 65,535][18]16%hu0 - USHRT_MAX
int、
signed signed int
基本带符号整数类型,至少包含 [-32,767, +32,767][18]16%i 或 %dINT_MIN - INT_MAX
unsigned unsigned int基本无符号整数类型,至少包含 [0, 65,535] [18]16%u0 - UINT_MAX
long long int 、
signed long signed long int
长符号整数类型,至少包含 [-2,147,483,647, +2,147,483,647][18]32%li 或 %ldLONG_MIN - LONG_MAX
unsigned long unsigned long int长无符号整数类型,至少包含 [0, 4,294,967,295] [18]32%lu0 - ULONG_MAX
long long long long int 、
signed long long signed long long int
长有符号整数类型,至少包含 [-9,223,372,036,854,775,807, +9,223,372,036,854,775,807]范围( 自 C99 标准版本开始指定)64%lli 或 %lldLLONG_MIN -LLONG_MAX
unsigned long long unsigned long long int长无符号整数类型,至少包含 [0,18,446,744,073,709,551,615] (自 C99 标准版本开始指定)[18]64%llu0 - ULLONG_MAX
float实数浮点类型,通常称为单精度浮点类型。实际属性未指定(最小限制除外);但在大多数系统中是 IEEE 754 单精度二进制浮点格式(32 位)[18]-%f %F
%g %G
%e %E
%a %A
-
double实数浮点类型,通常称为双精度浮点类型。实际属性未指定(最小限制除外);但在大多数系统中是 IEEE 754 双精度二进制浮点格式(64 位)[18]-%lf %lF
%lg %lG
%le %lE
%la %lA
-
long double实数浮点类型,通常映射为扩展精度浮点数格式。实际属性未指定。它可以是 x86 扩展精度浮点格式(80 位,但在内存中通常是 96 位或 128 位,带填充字节)、非 IEEE "double"(128 位)、IEEE 754 四倍精度浮点格式(128 位),或与 double 相同[18]-%Lf %LF
%Lg %LG
%Le %LE
%La %LA
-

整数类型的实际大小因实现而异,标准C仅要求数据类型之间的大小关系,以及每个数据类型的最小大小,具体关系要求是:

long long不得小于long,long不得小于int,int不得小于short

short和nt的最小大小为16位,long的最小大小为32位,long long必须至少包含64位

int类型应该是目标处理器效率最高的整数类型,提供了很大的灵活性[43]。在实际应用中,char通常是8位大小,short通常是16位大小(无符号类型也是如此)。这对于多数平台都成立,例如1990年代的SunOS 4 Unix、Microsoft MS-DOS、现代Linux以及Microchip MCC18用于嵌入式8位PIC微控制器。浮点类型(float)的实际大小和行为也因实现而异。唯一的要求是long double不得小于double,double不得小于float[18]

字符类型
关键字说明最小大小(位)格式化字符串范围
char机器中可寻址的最小单位,可包含基本字符集。它是一种整数类型,实际类型可以是有符号或无符号[18]8%cCHAR_MIN - CHAR_MAX
signed char大小与 char 相同,但保证是带符号的,至少包含 [-127, +127][18] 8%c(或 %hhi,用于数值输出)SCHAR_MIN - SCHAR_MAX[44]
unsigned char大小与 char 相同,但保证无符号,至少包含 [0, 255] [18]8%c(或 %hhu,用于数值输出)0 - UCHAR_MAX

值得注意的是,由于char的大小始终是最小支持的数据类型,因此除了位字段之外,其他数据类型不能更小,所以char的最小大小为8位。POSIX要求char的大小也为8位[45]。标准C中的各种规则使得unsigned char成为用于存储任意非位字段对象的数组的基本类型。

布尔类型

C99增加了布尔(True/False)类型“_Bool”。此外,<stdbool.h>头文件定义了bool作为该类型的别名,并提供了true和false的宏。“_Bool”的功能与普通整数类型类似,但有一个例外:对“_Bool”的赋值如果不是0(false),则存储为1(true)。这种规定是为了避免在隐式缩小转换中出现整数溢出[18]。例如,在以下代码中

unsigned char b = 256; if (b) { /* 代码 */ }

如果unsigned char的大小为8位,变量b的值将为false。这是因为值256不适配布尔数据类型,导致只有它的低8位被使用,从而产生一个零值。不过,改变类型后,之前的代码就会正常运行:

_Bool b = 256; if (b) { /* 代码 */ }

“_Bool”类型还能确保真值之间恒相等

_Bool a = 1, b = 2; if (a == b) { /* 此部分代码将正常运行 */ }

void 类型

void类型表示没有值的数据类型,通常用于函数返回值[46]。它通常用于以下三种情况下:

函数返回为空:C语言中函数可以不返回值,或者可以说它们返回空,不返回值的函数的返回类型为空[47]。函数参数为空:C语言中函数可以不接受任何参数,不带参数的函数可以接受一个 void[47]。指针指向 void:类型为void*的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型[48]

枚举类型

枚举(Enumerated)用于定义一组具有离散值的常量,它可以让数据更简洁,更易读。枚举类型通常用于为程序中的一组相关的常量取名字,以便于程序的可读性和维护性。定义一个枚举类型,需要使用enum关键字,后面跟着枚举类型的名称,以及用大括号{}括起来的一组枚举常量。每个枚举常量可以用一个标识符来表示,也可以为它们指定一个整数值,如果没有指定,那么默认从0开始递增。它在C语言实现中是以int类型储存的[49]。举个例子:

// 1.未定义整数值 enum a { b , c , d }; enum a foo; foo = b; if(foo != c) // 等同于if(foo != 1) { do_something(); } // 2.定义整数值 enum colour {red = 100,blue = 700,yellow = 200};

派生类型

C语言允许构造出任意数量的派生类型。这些成员可以是任意对象或函数类型。C语言中的聚合数据类型有六种:数组类型、指针类型、结构体类型、联合类型、函数类型和原子类型[42]

数组类型

数组(Arrays)是相同类型的值的集合,在内存中连续存储。除void和函数类型外,每种类型T都可以存在"由N个类型元素组成的数组"类型,即该类型数组。大小为N的数组由从0到N-1(包括N-1)的整数索引[49]

数组中的字符串
数组中的字符串

下面是一个简单的例子:

int cat; // 10个元素的数组,每个元素类型都是int

数组可以多维初始化,但不能多维赋值。

二维数组图示
二维数组图示
初始化数组的两种方法
初始化数组的两种方法

数组将指向第一个元素的指针传递给函数。多维数组的定义为数组的数组,除了最外层维度之外,所有维度定义时都必须确定常量大小[49]

int a; // 10个元素的数组,每个元素类型是“有8个元素的数组”

指针类型

指针(Pointers)是一种数据类型,它存储了特定类型变量的内存地址。对于每一种数据类型T,都存在与之对应的指向T类型的指针类型。在进行类型声明时,需要在变量名之前加上一个星号(*)来声明指针类型,而这个星号前后的空格是可选的。星号告诉编译器正在声明一个指针,而不是一个普通的变量。指针的值是一个内存地址,它指向了存储在内存中的特定类型的数据[49]

char *square; long *circle; int *oval;

也可以为指针数据类型声明指针,从而创建多个间接指针,如char**和int***,包括数组类型的指针。后者不如数组指针常见,其语法也比较容易混淆[49]

char *pc; // 由10个指向char的指针元素组成的数组 char (*pa); // 指向有10个char元素数组的指针

元素pc是个数组元素,包含10个指针元素,每个指针元素的大小是指针类型的大小(在大多数平台上通常是4或8字节),与指向char的指针类型大小无关。元素pa是一个指向数组的指针,所以pa的大小即一个char类型等大小,即4或8字节;而*pa 是一个指向数组的指针,指向一个有10个char元素的数组,所以其大小是数组的大小,为10字节。

结构体类型

结构体(Structures)将多个可能具有不同数据类型的数据项的存储汇集到一个由单个变量引用的内存块中。以下示例声明了数据类型struct birthday,其中包含了一个人的姓名和生日。结构体定义后,是对变量John的声明,该变量分配了所需的存储空间[17]

struct birthday { char name; int day; int month; int year; }; struct birthday John{"John Doe", 8, 15, 2000};

结构体的内存布局是由每个平台的语言实现决定的,但有一些限制:第一个成员的内存地址必须与结构体本身的地址相同;可以使用复合字面量来初始化或分配结构体。函数可以直接返回一个结构体,但是这种方式在运行时通常不太高效。自C99以来,结构体最后一个成员也可以是柔性数组[49]。实际运用中,还常常使用包含指向自身类型的结构体指针的结构体来构建链式数据结构

struct node { int val; struct node *next; };

联合类型

联合类型(Unions)是一种特殊的构造,允许使用不同类型的来访问同一内存块。例如,可以声明一个数据类型的联合,以允许将相同的数据读取为整数、浮点数或任何其他用户声明的类型[49]。在下面的示例代码中,联合u的总大小是u.s的大小——这恰好是u.s.u和u.s.d大小的总和,而且s大于i和f。当将某个值赋给u.i,如果u.i小于u.f,则可能会保留u.f的某些部分。值得注意的是,从联合中读取成员与强制转换不同,因为成员的值不会被转换,只是被读取。联合类型不被认为是聚合类型,因为在任一时刻下,联合中只有一个成员可以具有值[41]

union { int i; float f; struct { unsigned int u; double d; } s; } u;

函数类型

函数类型(Functions)是指返回类型已指定的函数,其特征由返回类型和参数的数量、类型决定。如果函数的返回类型是T,它就被称为“返回T的函数”。函数类型实际上定义了指向函数的指针,函数指针存储函数的入口地址,类似于其他指针存储的内存单元中的地址值[42]。如下面的例子:

#include <stdio.h> void say_hello(const char *str) { printf("Hello %s\n", str); } int main(void) { void (*f)(const char *) = say_hello; f("Guys"); return 0; }

其中,say_hello是一种函数类型,而函数类型和数组类型类似,做右值使用时自动转换成函数指针类型,所以可以直接赋给f,当然也可以写成void (*f)(const char *) = &say_hello;,把函数say_hello先取地址再赋给f,就不需要自动类型转换了。关于函数的进一步说明,请查看核心编程概念与技术章节。

原子类型

原子类型(Atomic)是由_Atomic指定的类型。原子类型是一种条件特性,出现 _Atomic 限定符就表示原子类型[42]。原子类型说明符使用方法如下:

_Atomic ( type_name )

值得注意的是,与原子类型相关的属性只对左值表达式有意义。如果_Atomic关键字后面紧跟一个左括号,那它将被认为是一个类型指定符,而不是一个类型限定符[42]

语句和语块

C语言的语句和块是构建程序的基本单元。语句是一系列指令或操作,用于执行特定的任务。语法结构规定了如何组织和编写这些语句,以便创建有效的C程序。块允许将多个语句组织在一起,形成一个逻辑单元,从而使代码更加清晰和可读[49]。在C语言中,语句和块的组合方式构成了程序的结构和逻辑流程,允许程序员实现各种功能和操作。通过合理地使用不同类型的语句和块,可以构建出高效、清晰和功能完备的C程序[42]

语句

在C语言中,语句(Statement)是编程中的基本构建块,它代表了一种执行动作或操作的指令。C语言中的每条语句以分号“;”结束。语句可以是简单的表达式、赋值、函数调用,也可以是控制流语句,如条件语句和循环语句[49]。简单的语句类型包括:

表达式语句:包含一个表达式,并以分号结尾。例如:x = 10;赋值语句:将一个值赋给一个变量。例如:y = x + 5;函数调用语句:调用一个函数并执行其中的代码。例如:printf("Hello, World!");

此外还有控制流语句,用于控制程序的执行流程,使程序能够根据条件或循环来做出不同的决策。所以他可以影响程序的执行流程,包括条件语句(if、else)、循环语句(for、while、do-while)等[50]

if语句

if语句包括判断指定一个或多个要评估或测试的条件,条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的)。注意,C 语言把任何非零和非空的值假定为 true,把零或 null 假定为 false[51]

if语句流程图
if语句流程图

以下是一个if语句的代码示例

#include <stdio.h> if(boolean_expression) { /* 如果布尔表达式为真将执行的语句 */ } else { /* 如果布尔表达式为假将执行的语句 */ }

条件运算符 ? :,可以用来替代 if...else 语句。它的一般形式如下:

Exp1 ? Exp2 : Exp

switch语句

一个 switch 语句允许测试一个变量等于多个值时的情况。每个值称为一个 case,且被测试的变量会对每个 switch case 进行检查[51]

switch语句程图
switch语句程图

switch代码示例如下:

#include <stdio.h> switch(expression){ case constant-expression : statement(s); break; /* 可选的 */ case constant-expression : statement(s); break; /* 可选的 */ /* 您可以有任意数量的 case 语句 */ default : /* 可选的 */ statement(s); }

循环语句

有的时候,可能需要多次执行同一块代码。C语言提供了两种主要的循环语句,用于重复执行一段代码块,直到满足特定条件为止,这些循环语句是:for 循环、while 、do-while 循环[6]

for语句

for循环是一种常用的循环控制结构,它允许编写一个在满足条件的情况下重复执行的循环[6]

for 循环结构
for 循环结构

示例代码如下:

for ( init; condition; increment ) { statement(s); }

该代码中 for 循环的控制流是:init 会首先被执行,且只会执行一次。这一步允许声明并初始化任何循环控制变量,或者可以不在这里写任何语句,只要有一个分号出现即可。接下来,会判断 condition:如果为真,则执行循环主体;如果为假,则不执行循环主体,且控制流会跳转到紧接着 for 循环的下一条语句。在执行完 for 循环主体后,控制流会跳回的 increment 语句。该语句允许更新循环控制变量,此处也可以留空,只要在条件后有一个分号出现即可。然后条件再次被判断。如果为真,则执行循环,这个过程会不断重复,直到在条件变为假时,for 循环终止[6]

while语句

只要给定的条件为真,C 语言中的 while 循环语句会重复执行一个目标语句。在这里,被循环的可以是一个单独的语句,也可以是几个语句组成的代码块。执行条件可以是任意的表达式,该条件当为任意非零值时都为 true。当条件为 true 时执行循环。 当条件为 false 时,退出循环,程序流将继续执行紧接着循环的下一条语句[6]

简单 while 循环的结构
简单 while 循环的结构

示例代码如下:

#include <stdio.h> while(condition) { /* 被循环执行的语句 */; } /* 2.do while语句 */ do { /* 被循环执行的语句 */; }while( condition );

do-while语句

和for 和 while 循环不同,它们是在循环头部测试循环条件。在 C 语言中,do-while 循环是在循环的尾部检查它的条件。do-while 循环与 while 循环类似,但是 do-while 循环会确保至少执行一次循环[6]

示例代码如下:

#include <stdio.h> do { /* 被循环执行的语句 */; }while( condition );

语块

语块(Block)是由一组语句组成的代码段,用花括号“{ }”括起来。语块可以包含多条语句,将它们作为一个逻辑单元组织在一起。在C语言中,块通常用于构建函数体、条件分支和循环体等[49]。它有以下特点:

语块中的语句按照顺序执行,可以在块内声明局部变量,这些变量的作用域仅限于块内部。可以嵌套,即一个块可以包含另一个块。内层块可以访问外层块的变量,但外层块不能访问内层块的变量。可以帮助实现更复杂的控制流,例如在if语句或循环中使用块来组织多条语句。也常用于定义函数的函数体,函数体是一个块,其中包含函数的实际操作[49]

在下面的示例中,if语句和内部的语块都展示了块的使用。语块有助于组织代码、限定变量作用域,并在控制流结构中提供更大的灵活性。

#include <stdio.h> int main() { int x = 5; if (x > 0) { printf("x is positive.\n"); // 这里的块包含了一条语句 } else { printf("x is not positive.\n"); } { int y = 10; // 这个块内定义的变量 y 的作用域仅限于此块 printf("y = %d\n", y); } return 0; }

核心编程概念与技术

良好的编程技术是构建可维护、高效和灵活的软件系统的基石,此处主要介绍函数与模块化以及内存管理这两个关键方面。C语言的模块化和函数、以及内存管理能力赋予了开发者更大的灵活性和控制权,同时也要求开发者具备良好的编程实践和责任感,以确保程序的正确性和稳定性[46][53]。代码组织结构上来说,模块化即功能细化,可以使逻辑更清晰,可读性更好,维护成本低;底层逻辑看,内存管理是组织存储数据和运算的最基本手段,不好的内存管理常常会导致产品崩溃,数据被盗等等问题[46][53]

函数与模块化

函数与模块化是程序设计的基本构建块,强调将复杂问题分解为更小、更易管理的部分。通过函数,我们能够将代码逻辑封装成独立的、可重用的单元,从而提高了代码的可读性、可维护性和可测试性。模块化则进一步将函数组织为模块,每个模块有特定的功能,模块之间通过接口交互。这种结构化的方法使得开发者能够专注于解决特定问题,同时也促进了团队协作和代码复用[6]

在函数方面,C语言允许开发者定义自己的函数,通过函数名和参数列表来调用这些函数。函数可以接受参数,执行一系列操作,然后返回一个值。这样的抽象使得开发者可以将代码逻辑封装起来,只需要关注函数的输入和输出。模块化编程则进一步扩展了这种思想,将函数分组为模块,每个模块可以包含多个函数。每个模块都有特定的功能,模块之间通过接口进行通信。这种分解使得程序更加模块化,每个模块都可以独立开发和测试,最终组合在一起形成完整的程序[6]

函数的使用

在C语言中,函数是一个执行特定任务的代码块,它能够接受输入参数、执行操作,并返回一个值[6]。下面是关于函数的定义和声明的详细说明:

函数定义

定义函数是指实现函数的操作,即编写函数体内的代码。一个函数定义包括以下组成部分:返回类型:指定函数的返回值类型,如整数、浮点数等。函数名:函数的标识符,用于在代码中调用函数。参数列表:指定函数接受的参数类型和参数名。函数体:包含实际的代码执行操作。返回语句:用于将结果返回给调用者[6]

C 语言中的函数定义的一般形式如下:

return_type function_name( parameter list ) { body of the function; }

函数声明

函数声明是指提前告知编译器有一个函数存在,但并不提供函数的实际实现。这在多个文件中使用同一个函数时非常有用,编译器在链接时会找到实际的函数定义[6]。函数声明包括返回类型,函数名和参数列表[6],比如:

int add(int x, int y); // 声明一个名为 add 的函数,接受两个整数参数,返回一个整数 float calculateAverage(float values[], int size); // 声明一个名为 calculateAverage 的函数,接受一个浮点数数组和一个整数参数,返回一个浮点数 void displayMessage(); // 声明一个名为 displayMessage 的函数,不接受任何参数,也不返回任何值(void表示无返回值)

函数调用

函数调用是指在程序中使用函数来执行特定任务。调用函数涉及以下步骤[6]

1. 调用函数:通过函数名和参数列表调用函数。例如:result = add(5, 3);

2. 传递参数:将实际参数传递给函数。函数的参数可以是值、指针、数组等。

3. 执行函数:函数体中的代码将根据传递的参数执行操作。

4. 返回值:函数执行后,可以通过`return`语句将结果返回给调用者[6]

其调用的语法为:

return_value_variable = function_name(parameter list);

其中:

return_value_variable: 用于接收函数的返回值的变量。这个变量的类型必须与函数的返回类型相匹配。function_name: 要调用的函数的名称。parameter list: 一对括号内,包含传递给函数的实际参数,多个参数之间用逗号分隔。

举个例子:

int sum = add(5, 3); // 调用名为 add 的函数,传递参数 5 和 3,将返回值赋给 sum float avg = calculateAverage(data, size); // 调用名为 calculateAverage 的函数,传递数组 data 和参数 size,将返回值赋给 avg displayMessage(); // 调用名为 displayMessage 的函数,不传递任何参数

请注意,函数调用的实际参数必须与函数声明中的参数类型和顺序匹配。调用函数时,实际参数会被传递给函数的形式参数(在函数定义中使用的参数),函数内部将使用这些形式参数执行相应的操作。函数调用的结果(返回值)可以被赋值给变量或用于其他计算。如果函数没有返回值(返回类型为 void),则函数调用只需要函数名和实际参数列表,不需要将结果赋给变量[6]

模块化编程

模块化编程是一种将大型程序分解为更小、更易管理的模块的方法。每个模块负责特定的任务,使代码更加组织化、可读性更强、可维护性更高。模块化编程强调以下几点:

分而治之:将大问题分解为多个小问题,每个模块解决一个特定问题。高内聚低耦合:模块内部的元素紧密相关,模块之间的依赖性较低。接口定义:每个模块应该有清晰的接口,明确定义输入和输出。可复用性:合理设计的模块可以在不同项目中重复使用,提高开发效率。

模块化编程可以减少代码的复杂性,便于团队协作,允许并行开发,以及更容易进行调试和维护[53]

内存管理

内存管理是确保程序正确、高效地使用计算机内存资源的关键。它涉及到动态内存分配和释放,以及避免内存泄漏和越界访问等问题。通过合理地分配和释放内存,程序可以有效地利用计算机的内存资源,降低资源浪费。同时,内存管理也需要开发者理解指针的概念和用法,以确保数据在内存中正确存储和访问。C语言提供了两主要的方式来为对象分配内存,即静态内存管理理和动态内存管理[54]

静态内存管理

静态内存管理是指在编译时为变量和数据分配固定大小的内存空间。在C语言中,全局变量和局部静态变量都使用静态内存分配。这些变量的内存分配在程序启动时就完成,其生命周期与整个程序运行周期相同。静态内存分配发生在编译时,变量在程序生命周期内占据固定的内存位置。它适用于需要在整个程序中保持数据的情况,如配置参数、常量等。此外静态内存分配的空间较小,所以不适合存储大量数据或动态变化的数据[49]

动态内存分配

动态内存管理是指在程序运行时根据需要分配和释放内存空间。动态内存分配允许在运行时根据具体情况分配所需大小的内存块,但需要手动释放分配的内存,以避免内存泄漏[49]。它适用于需要在程序运行期间根据需求分配和释放内存的情况,如动态数组、链表字符串等。而且内存分配的大小可以根据需求动态变化,但需要手动管理内存的释放,以防止内存泄漏。在使用动态内存管理时,要谨慎处理内存释放,以免产生悬挂指针或野指针等问题。在C语言中,可以使用标准库函数malloc和free来实现动态内存分配和释放[55]

堆上存储数据图示
堆上存储数据图示

标准库函数

C语言使用库作为其主要的扩展方法。在C语言中,库是一组包含在单个“存档”文件中的函数。每个库通常都有一个头文件,其中包含库中包含的函数的原型,并声明了与这些函数一起使用的特殊数据类型和宏符号[49]。如果程序想要使用库,必须包含库的头文件,并且库必须与程序链接在一起,可以使用编译器标志来完成链接(例如,-lm,意为“链接数学库”)[54]。最常见的C库是C标准库,它由ISO和ANSI C标准指定,并随每个C实现一起提供(针对嵌入式系统等有限环境的实现可能仅提供标准库的子集)。C标准库支持流输入和输出、内存分配、数学、字符字符串和时间值。标准头文件(例如,stdio.h)为这些标准库的接口规定提供了详细的说明[31]

文件说明文件说明
<assert.h>断言相关<time.h>时间相关
<ctype.h>字符类型判断<iso646.h>ISO 646变体字符集相关
<errno.h>标准报错机制<wchar.h>宽字符串处理函数
<float.h>浮点运算<wctype.h>按类型对宽字符进行分类或在大小写字母之间进行转换
<limits.h>各种体系结构限制<complex.h>复数操作
<locale.h>本地化接口<fenv.h>控制浮点数环境
<math.h>数学函数<inttypes.h>精确的宽度整数类型操作相关
<setjmp.h>跨函数跳转<stdbool.h>布尔数据类型相关
<signal.h>信号(类似UNIX的信号定义)<stdint.h>精确的宽度整数类型相关
<stdarg.h>可变参处理<tgmath.h>类型通用的数学函数
<stddef.h>一些标准宏定义<stdalign.h>查询和指定对象的数据结构对齐方式
<stdio.h>标准I/O库<stdatomic.h>用于查询和指定对象的数据结构对齐方式
<stdlib.h>标准工具库函数<threads.h>管理多个线程、互斥锁和条件变量的函数
<string.h>ASCII字符串及任意内存处理函数<uchar.h>操作Unicode字符的类型和函数

具体函数可根据需要查阅文档并进行学习。这些函数可以帮助程序员更轻松地进行各种任务。最常用的包括输入输出、字符串处理、数学计算三种函数,下面简单介绍一下用法。

输入输出函数

C语言提供了一系列输入输出函数,用于与用户进行交互,从文件中读取数据或将数据写入文件[57]。其中,最常用的输入输出函数是printf和scanf。

printf

printf函数用于格式化输出到标准输出设备(通常是屏幕)。它可以将文本和变量的值格式化后输出[57]

int num = 10; printf("The number is: %d\n", num);

scanf

scanf函数用于格式化输入,从标准输入设备(通常是键盘)读取数据并存储到变量中[57]

int age; printf("Enter your age: "); scanf("%d", &age);

格式化输出和输入

可以使用格式控制符来指定输出和输入的格式。例如,`%d`表示整数,`%f`表示浮点数,`%s`表示字符串等[16]

float price = 19.99; printf("The price is: $%.2f\n", price); char name; printf("Enter your name: "); scanf("%s", name);

字符串处理函数

C语言提供了一些字符串处理函数,用于操作和处理字符数组(字符串)[57]。一些常用的字符串处理函数包括strcpy,strcat和strlen。

strcpy

strcpy函数可以用于将一个字符串复制到另一个字符串[57]

char source[] = "Hello, World!"; char destination; strcpy(destination, source);

strcat

strcat函数可以用于将一个字符串连接到另一个字符串的末尾[57]

char str1 = "Hello, "; char str2[] = "World!"; strcat(str1, str2);

strlen

strlen函数用于计算字符串的长度(不包括空字符)[57]

strlen函数判断在“\0”停止
strlen函数判断在“\0”停止

char myString[] = "Programming"; int length = strlen(myString);

数学函数

C语言提供了许多数学函数,位于数学库<math.h>中,用于执行各种数学运算[57]。以下是一些常用的数学函数:

sqrt

sqrt函数可以计算提供数的平方根[57]

#include <math.h> double result = sqrt(25.0);

sin和cos

sin和cos两个函数可以用于计算正弦余弦[57]

#include <math.h> double angle = 45.0; double sineValue = sin(angle * M_PI / 180.0); double cosineValue = cos(angle * M_PI / 180.0);

预处理器和编译过程

在C语言中,预处理器和编译过程是代码从源代码到可执行文件的重要步骤。它们是在程序构建过程中的前两个阶段,负责预处理源代码和将其翻译为可执行的机器代码。预处理器在源代码级别进行文本处理和宏展开,编译过程将中间代码翻译为汇编语言和机器码,并最终生成可执行文件。这两个阶段在C语言编程中起着重要作用,有助于确保代码的正确性和可执行性。

编译过程流图
编译过程流图

预处理

预处理(Preprocess)是C语言编译过程的第一个阶段,即对源代码进行预处理,进行一些文本替换和宏展开,生成经过处理的中间代码。预处理指令以 `#` 开头,例如 “#include”用于包含头文件、“#define”用于定义宏等[9]。预处理器在生成中间代码后,将其传递给编译器进行编译。

编译

编译(Compile)是C语言构建过程的第二个阶段。在这个阶段,编译器将预处理器生成的中间代码翻译成汇编语言,然后再将汇编语言翻译为机器码,最终生成可执行文件。编译过程包括以下主要步骤:

词法分析(Lexical Analysis):将源代码分解为一个个标记(tokens),如变量、运算符、关键字等。语法分析(Syntax Analysis):根据语法规则检查标记的组合方式,形成语法树。语义分析(Semantic Analysis):确保代码在语义上是合法的,并进行类型检查等。代码优化:对生成的中间代码进行优化,以提高程序的性能和效率。代码生成:将优化后的中间代码翻译为汇编语言。汇编:将汇编语言翻译为机器码。链接(Linking):将多个源文件的机器码合并成一个可执行文件,包括解析符号引用、解决跳转等。

常用工具

常用的预处理器和编译器有:

gcc预处理器: GNU Compiler Collection(GCC)是一种广泛使用的预处理工具和C语言编译器,它处理 #include、#define 等预处理指令,展开宏,进行条件编译等,将C源代码编译成汇编代码、链接库文件,并生成可执行文件[3]

clang预处理器: C语言编译器前端,它也包含了预处理器。与GCC类似,它可以用于处理预处理指令和宏展开等操作,并可以提供了高质量的诊断信息和错误报告,将C代码编译为高效的机器码[4]。Visual C++预处理器: Microsoft Visual Studio中的编译工具,用于将C代码编译为可执行文件,提供了丰富的开发环境和调试功能[58]CMake:跨平台构建工具,用于生成项目构建脚本和构建文件。它可以生成适用于各种编译器和操作系统的构建配置[59]

实际应用

C语言应用领域
C语言应用领域

系统编程

在系统编程中,C语言广泛用于开发操作系统、设备驱动程序和系统工具。C语言的文件操作函数,如fopen、fclose、fread、fwrite等,可以实现文件的读写、创建和管理。fork、exec、wait等进程控制函数可以管理进程的创建、执行和等待[57]。这些特点让C语言被广泛用于开发系统工具,如编译器、解释器、调试器等,帮助程序员更好地开发和维护代码。比如Linux内核是由C语言编写的[60],以及Windows操作系统中的进程管理功能和它的大部分内核,也是通过C语言编写的[61]

嵌入式开发

嵌入式系统中的C语言应用非常常见,因为C语言具有高效、可移植性和直接硬件控制能力。它允许程序员直接控制硬件,从而实现低功耗的设计,延长嵌入式设备的电池寿命。可以用于编写实时系统,确保嵌入式系统按照严格的时间要求执行任务[46]。比如Arduino,它是一个广泛用于嵌入式开发的平台,其编程语言就是基于C/C++。开发者可以使用Arduino IDE编写C/C++代码,与各种传感器执行器进行交互,从而创建物联网设备、机器人、家用自动化系统等[62]。还有实时操作系统(RTOS)如FreeRTOS[63]等,可以在嵌入式系统上运行,它们的内核也通常是使用C语言编写的。

Arduino单板
Arduino单板

游戏开发

C语言在游戏开发领域仍然有一定的影响力,尽管现代游戏引擎往往使用更高级的语言。使用C语言和图形库,如OpenGL,可以实现游戏中的图形渲染和绘制。它可以用于实现游戏中的物理引擎,模拟物体的运动、碰撞等行为。此外游戏的核心逻辑部分通常由C语言编写,包括游戏规则、AI算法等。比如Doom,它是1993年的3D第一人称射击游戏,使用C语言编写了其引擎、游戏逻辑和渲染代码,为游戏开发铺平了道路[65]。Allegro也是一个用于游戏开发的C/C++图形库,它支持2D图形、声音、输入等,许多独立游戏和小型游戏项目使用Allegro库来实现游戏功能[66]

Doom海报
Doom海报

Hello World示例代码

“Hello,World”示例最早出现在《C程序设计语言》的第一版中,已经成为大多数编程教材中引入程序的范例。此外,当涉及到编程时,"Hello World"程序通常是一个入门级的示例程序,用于展示一个基本的程序结构和输出功能。以下是C语言中的一个简单的"Hello World"程序示例[28]

#include <stdio.h> int main(void) { printf("hello, world\n"); }

这个程序使用了C语言的标准输入输出库<stdio.h>,并在屏幕上输出了一条简单的信息:"Hello, World!"。程序的第一行包含一个预处理指令,用#include表示,这个操作使编译器用stdio.h标准头文件的整个文本替换该行。[28]。第三行定义一个名为main的函数。在C程序中,主函数有特殊的用途;运行时环境调用主函数来开始程序执行。类型说明符int表示main函数求值的结果返回给调用者的值是整数。参数列表中的关键字void表示此函数不接受任何参数[28]。大括号的开头表示开始定义主函数。第五行调用一个名为printf的函数,该函数为系统库提供。分号“;”表示该语句终止[28]。大括号的闭合表示主函数代码的结束。根据C99规范及更高版本,与其他函数不同,主函数在达到终止该函数的“}”时会隐式返回一个值为0的值(C99以前需要显式的“return 0;”语句)。运行时系统将其理解为成功执行并退出代码[54]

安全性和低级编程

在C语言中,安全性与低级编程是一项关键的考虑因素,因为C语言提供了较高的灵活性和底层控制,但同时也使得编程者需要更加小心谨慎,以确保代码的安全性和稳定性[68]。主要包括以下问题:

内存管理风险:C语言允许直接操作内存,包括动态内存分配和指针操作。然而,不正确的内存操作可能导致内存泄漏、越界访问、悬挂指针等问题。开发者需要确保正确地分配和释放内存,避免出现悬挂指针(指向已释放内存的指针)或者野指针(指向未知内存的指针),从而避免程序崩溃或安全漏洞[68]。缓冲区溢出:C语言中的缓冲区溢出是一种常见的安全漏洞。当写入超出缓冲区边界的数据时,可能会覆盖相邻内存区域,导致程序崩溃或执行恶意代码。攻击者可以利用缓冲区溢出修改计算机的内存,以破坏或控制程序的执行。为了避免这种问题,开发者需要使用安全的字符串函数(如strncpy而非strcpy)或者手动检查输入数据的长度[68]

缓冲区溢出图示
缓冲区溢出图示

整数溢出:C语言的整数类型没有自动溢出检查,因此在进行数值计算时,开发者需要小心处理溢出情况,以防止不正确的结果或安全漏洞。[68]安全编程实践:编写安全的C代码需要遵循一系列最佳实践,如避免使用不安全的函数(如gets)、验证输入、使用安全的字符串函数、避免硬编码敏感信息等。开发者还可以使用静态分析工具和代码审计来发现和修复潜在的安全问题[70]

影响和地位

C语言在系统级编程领域具有重要地位。由于C语言的底层性质和对硬件的直接控制能力,它成为开发操作系统、设备驱动程序和嵌入式系统的首选语言。许多操作系统的内核和底层组件都是用C语言编写的,包括UNIXLinux。此外C语言在不同硬件平台之间具有很好的可移植性,这使得开发者可以编写一次代码,然后在多个平台上运行,从而大大简化了跨平台开发。C语言还因其高效的性能而受到赞誉,特别适合需要高性能的应用程序,如图像处理、游戏引擎等。C语言的发展还推动了编译器和开发工具链的进步。很多编程语言的编译器和工具都是用C语言编写的,这包括C++、Java等。C语言的语法和结构直接影响了许多后续语言的设计[16]

许多程序员在职业生涯的早期都会学习和使用C语言,因为它不仅教授了基本编程概念,还帮助开发者理解计算机底层的工作原理。所以它被广泛用于编程教育,特别是在计算机科学和工程学的课程中。学习C语言可以帮助学生培养良好的编程思维、算法和数据结构的理解,以及问题解决能力[16]

总之,C语言以其高度的灵活性、可移植性和性能,对计算机科学和软件开发领域产生了深远的影响。它在系统编程、跨平台开发、编译器工具链、软件教育等方面都占据着重要地位,并为后续编程语言的发展铺平了道路[16]

衍生语言

C语言对许多后来的编程语言产生了影响,特别是C++[71]C#[72]等。C++和C#都是在C语言基础上发展而来,它们在一些方面有共同之处,同时也有显著的差异。C++是C语言的扩展,引入了面向对象编程(OOP)的概念。它保留了C语言的底层特性,同时添加了类、继承、多态等面向对象的功能。C++适用于需要高性能、底层控制和面向对象编程的应用,如游戏开发、系统编程等[73]。而C#是微软开发的一种现代编程语言,它也支持面向对象编程,并且更加注重开发效率和可移植性。C#通常用于开发Windows应用程序、Web应用程序和移动应用程序。它提供了丰富的库和框架,使开发变得更加简单和高效[72]

参考资料 73

  1. 参考 1
  2. https://dl.acm.org/doi/pdf/10.1145/234286.1057834
  3. GCC, the GNU Compiler Collection — GNU Project
  4. Clang: a C language family frontend for LLVM — LLVM
  5. C language documentation — Microsoft Learn
  6. 参考 6
  7. Imperative programming: Overview of the oldest programming paradigm — Digital Guide
  8. 参考 8
  9. 参考 9
  10. https://www.ambchina.com/data/upload/image/20211124/%E4%B8%87%E7%89%A9%E4%BA%92%E8%81%94%E6%97%B6%E4%BB%A3%E7%9A%84%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8A%A5%E5%91%8A.pdf
  11. https://scc.ustc.edu.cn/zlsc/user_doc/html/userdocument-scc-ustc.pdf
  12. https://archive.org/details/byte-magazine-1983-08/page/n267/mode/2up
  13. Programming in C: A Tutorial — Lysator
  14. https://lshoshia.science.tsu.ge/C/Stephen%20G%20Kochan%20Programming%20in%20C%20%202005.pdf
  15. TIOBE Index — TIOBE
  16. 参考 16
  17. 参考 17
  18. ISO/IEC 9899:1999 specification — Open Standards
  19. PDP-7 Definition — Linfo
  20. https://www.usenix.org/system/files/login/issues/login_winter17_issue.pdf
  21. The Digital Equipment Corporation PDP-7 — Columbia Edu
  22. BCPL to B to C — Lysator
  23. https://www.cs.dartmouth.edu/~doug/reader.pdf
  24. Ken Thompson (sitting)-&-Dennis Ritchie at PDP-11 — Internet Archive
  25. https://citeseerx.ist.psu.edu/doc/10.1.1.138.35
  26. History of C — Cppreference
  27. https://stroustrup.com/sibling_rivalry.pdf
  28. https://archive.org/details/cchengxushejiyuy0000kern/page/n7/mode/2up?view=theater
  29. ANSI C 与 K&amp;R C — 北京大学数学科学学院
  30. C99 &amp; Numeric computing — Dr. Dobb's Journal
  31. ISO/IEC 9899:201x — ISO/IEC
  32. The Standard — ISO
  33. ISO/IEC 9899:201x (ISO C11) Committee Draft — Open Standards
  34. Revise spelling of keywords, proposal for C23 — Open Standards
  35. The constexpr specifier for object definitions — Open Standards
  36. Make false and true first-class language features, proposal for C23 — Open Standards
  37. Introduce the nullptr constant — Open Standards
  38. Not-so-magic - typeof for C — Open Standards
  39. WG14-N2601 : Annex X - IEC 60559 interchange and extended types — Open Standards
  40. WR14-N3042 : Introduce the nullptr constant — Open Standards
  41. C语言数据类型分类 — C语言中文网
  42. Programming languages — C — Internet Archive
  43. 64-Bit Programming Models: Why LP64? — What is UNIX?
  44. C and C++ Integer Limits — Microsoft learn
  45. Width of an Integer Type — GNU
  46. https://progforperf.github.io/Expert_C_Programming.pdf
  47. 不完全类型 — Oracle
  48. 指针声明 — Microsoft learn
  49. 参考 49
  50. C Control Flow Examples — Programiz
  51. 参考 51
  52. C 数据类型 — 菜鸟教程
  53. https://cread.jd.com/read/startRead.action?bookId=30469204&readType=1
  54. https://media.oiipdf.com/pdf/250dfbe5-4493-4cd5-b1fd-7ae91e1f64ea.pdf
  55. 参考 55
  56. 参考 56
  57. 参考 57
  58. Microsoft Visual C++ Redistributable latest supported downloads — Microsoft Learn
  59. Cmake
  60. 程序设计语言 — The Linux Kernel Archives
  61. Walkthrough: Creating Windows Desktop Applications (C++) — Microsoft Learn
  62. http://www.tup.tsinghua.edu.cn/upload/books/yz/090777-01.pdf
  63. http://images.china-pub.com/ebook8055001-8060000/8057851/ch01.pdf
  64. Arduino Uno Rev3 — Arduino Store
  65. 走进 C 语言 — 徐越的编程书
  66. Allegro5 — Github
  67. Doom — Steam
  68. 参考 68
  69. 什么是缓冲区溢出? — Cloudflare
  70. https://electrovolt.ir/wp-content/uploads/2022/09/MISRA-C_2012_-Guidelines-for-the-Use-of-the-C-Language-in-Critical-Systems-Motor-Industry-Research-Association-2013-2013.pdf
  71. 参考 71
  72. C# 语言介绍 — Microsoft Learn
  73. 参考 73
广告位:底部(ad-bottom)—— 请到中台「公共区块」编辑此内容