编程语言之C
2021-08-14 20:53:36 阿炯
C是一种通用的编程语言,广泛用于系统软件与应用软件的开发。于1969年至1973年间,为了移植与开发UNIX操作系统,由丹尼斯·里奇与肯·汤普逊,以B语言为基础,在贝尔实验室设计、开发出来。
C语言具有高效、灵活、功能丰富、表达力强和较高的可移植性等特点,在程序设计中备受青睐,成为最近年使用最为广泛的编程语言。目前,C语言编译器普遍存在于各种不同的操作系统中,例如Microsoft Windows、MacOS、Linux、Unix等。C语言的设计影响了众多后来的编程语言,例如C++、Objective-C、Java、C#等。
二十世纪八十年代,为了避免各开发厂商用的C语言的语法产生差异,由美国国家标准局为C语言订定了一套完整的国际标准语法,称为ANSI C,作为C语言的标准。二十世纪八十年代至今的有关程序开发工具,一般都支持符合ANSI C的语法。
C之父,Dennis Ritchie
20世纪70年代,肯·汤普逊为了使其设计的Unix系统更加高效,使用B语言的变种(即C语言)重写了Unix。在1978年,丹尼斯·里奇和布莱恩·柯林汉合作出版了《C程序设计语言》第一版,事实上即为K&R C标准。在此之后,制定标准的工作转移到ISO和ANSI。目前最新的C语言标准是C18:ISO/IEC 9899:2018(2018年6月修订)。
特性
C语言是一个有结构化程序设计、具有变量作用域(variable scope)以及递归功能的过程式语言。
C语言传递参数均是以值传递(pass by value),另外也可以传递指针(a pointer passed by value)。
不同的变量类型可以用结构体(struct)组合在一起。
只有32个保留字(reserved keywords),使变量、函数命名有更多弹性。
部分的变量类型可以转换,例如整数型和字符型变量。
透过指针(pointer),C语言可以容易的对存储器进行低端控制。
编译预处理(preprocessor),让C语言的编译更具有弹性。
在C语言中,程序从 main 开始执行。main 函数通过调用和控制其他函数进行工作。例如printf,程序员可以自己写函数,或从库中调用函数。在'return 0;'中使得 main 返回一个值给调用程序的壳层,表明程序是否成功运行。
一个C语言的函数由返回值、函数名、参数列表和函数体组成,函数体的语法和其它的复合的语句部分是一样的。
C语言的特色之一是:语言不负责内存边界检查,这是因为在运行时进行内存边界检查会造成性能问题,与UNIX哲学不符。此特性容易导致缓冲区溢出问题。然而部分编译器(如英特尔编译器)会出于安全性的考量,提供方法以进行运行时内存边界检查。
复合语句
C语言中的复合语句(或称语句块)的格式为:
{
语句;
语句;
/* ... */
}
复合语句可以使得几个语句从文法上变成一个语句。有时必须使用复合语句,否则会产生错误。例如,在运用循环语句的时候,如果循环体(即循环中执行部分)包含多个语句(以分号隔开),则必须使用花括号将他们合并成一个复合语句。如果不这么做,系统仅把第一个分号前的内容看做循环体。
需要注意的是,部分C编译器并不支持在任意位置使用复合语句。
条件语句
C语言有两种条件语句形式,分别是if和switch。
If 的格式如下:
if (运算式) // 如果
语句;
// 有时还会有 else:
else // 否则
语句;
表达式的值非零表示条件为真;如果条件为假,程序将跳过if处的语句,直接运行if后面的语句。但是如果if后面有else,则当条件为假时,程序跳到else处运行。if和else后面的语句可以是另个if语句,这种套叠式的结构,允许更复杂的逻辑控制流程得以实现。在一般情况下,else一定与最接近的if成对,因此常用括弧{}越过此限制。比较下面两种情况:
if (逻辑表达)
if (逻辑表达式)
语句;
else
语句;
下面是if的另一种格式if……else if……else……
if (逻辑表达式)
语句;
else if (逻辑表达式) //在下面可以加入多个“else if()”语句
语句;
else
语句;
if (逻辑表达式甲) {
if (逻辑表达式乙)
语句;
}
else
语句;
要注意这里的缩进和换行只用于方便阅读。编译器并不会根据缩进层级猜测 if 和 else 的对应关系。
switch通常用于对几种有明确值的条件进行控制。它要求的条件值通常是整数或字符。与switch搭配的条件转移是case。使用case后面的标值,控制程序将跳到满足条件的case处一直往下运行,直到语句结束或遇到break。通常可以使用default把其他例外的情况包含进去。如果switch语句中的条件不成立,控制程序将跳到default处运行;如果省略default子句,则直接运行下一语句。switch是可以嵌套的。
switch (值) {
case 甲:
case 乙:
语句段1; // 甲乙都会执行
// 更多语句…
break; // 跳转到 switch 末尾处
case 丙:
语句段2;
break;
default: // 无论如何都会匹配
语句段3;
}
简单的条件判断也可用?:
运算式?值1:值2;
如:a=b>c?b:c // 如果变数"b"的值大于变数"c" 把变数"b"的值赋予变数"a"
循环语句
C语言有三种形式的循环语句:
do
语句
while(判断式);
和:
while(判断式)
语句;
和:
for(起始化;判断式;运算式)
语句;
在while和for中,语句将运行到表达式的值为零时结束。在do...while语句中,循环将至少被运行一次。这三种循环结构可以互相转化:
for(起始化;判断式;运算式)
语句;
如果语句中不使用continue语句的话,相当于
起始化;
while (判断式) {
语句;
运算式;
}
当循环条件一直为真时,将产生死循环。
跳转语句
跳转语句包括四种:goto,continue,break和return。
goto 标记;
goto语句是无条件转移语句,且标记必须在当前函数中定义,使用“标记:”的格式定义。程序将跳到标记处继续运行。由于goto(特别是向回 goto 和长距离的 goto)容易产生阅读上的困难,所以对新手应该尽量少用。GCC 编译器拓展支持对指针 goto和宏内 goto,一定程度上增强了 goto 的可读性。
continue语句用在循环语句中,作用是结束当前一轮的循环,马上开始下一轮循环。
break语句用在循环语句或switch中,作用是结束当前循环,跳到循环体外继续运行。但是使用break只能跳出一层循环。在要跳出多重循环时,可以使用goto使得程序更为简洁。
当一个函数运行结束后要返回一个值时,使用return,return可以跟一个表达式或变量。如果return后面没有值,将运行不返回值。
指针
如果一个变量声明时在前面使用 * 号,表明这是个指针型变量。换句话说,该变量存储一个地址,而 *(此处特指单目运算符 *,下同。C语言中另有双目运算符 * 表示乘) 则是取内容操作符,意思是取这个内存地址里存储的内容。把这两点结合在一起,可将 int *a;看作是 “*a 解得的内容类型为 int”,对更复杂的声明也如此。指针是 C 语言区别于其他同时代高级语言的主要特征之一。
指针不仅可以是变量的地址,还可以是数组、数组元素、函数的地址。通过指针作为形式参数可以在函数的调用过程得到一个以上的返回值(不同于return z这样的仅能得到一个返回值。
指针是一把双刃剑,许多操作可以通过指针自然的表达,但是不正确的或者过分的使用指针又会给程序带来大量潜在的错误。
例如:
int *pi; // 指向整型数据的指针 pi
int * api[3]; // 由指向整型数据的指针构成的数组,长度为 3
char ** argv; // 指向一个字符指针的指针
struct { int member; } stinst,
* pst = & stinst;
// pst是一个指向一个匿名结构体的指针
储存在指针中的地址所指向的数值在程序中可以由 * 读取。例如,在第一个例子中, *pi 是一个整型数据。这叫做引用一个指针。
另一个运算符 &,叫做取地址运算符,它将返回一个变量、数组或函数的存储地址。因此看下面的例子:
int i, *pi; /* int and pointer to int */
pi = &i;
i 和 *pi 在程序中可以相互替换使用,直到 pi 被改变成指向另一个变量的地址。
当指针指向结构体时,可以使用运算符 -> 代替 *和. 的作用,如 (*p).m 与 p->m 等效。
字符串
C语言的字符串其实就是char型数组,所以使用字符串并不需要引用库。然而C标准库确实包含了用于对字符串进行操作的函数,使得它们看起来就像字符串而不是数组。使用这些函数需要引用头文件string.h。
文件输入/输出
在C语言中,输入和输出是经由标准库中的一组函数来实现的。在ANSI/ISO C中,这些函数被定义在头文件stdio.h中。
标准输入/输出
有三个标准输入/输出是标准I/O库预先定义的:
stdin 标准输入
stdout 标准输出
stderr 输入输出错误
下面的这个例子显示了一个过滤程序(filter program)是怎样构成的。
#include <stdio.h>
int main(int argc, const char * argv[]){
char c;
while ((c=getchar())!=EOF)
putchar(c);
perror("getchar() got EOF");
return -1;
}
函数
C语言的基本结构单位是函数。系统首先调用 main函数(主函数),通过函数的嵌套调用,再调用其他函数。函数可以是系统自带的函数,也可以是用户定义的函数。C语言中,不允许函数嵌套定义。
内存管理
C语言的特色之一是:程序员必须亲自处理内存的分配细节。大多数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语言的标准文档要求了一个平台移植C语言的时候至少要实现的一些功能和封装的集合,称为“标准库”,标准库的声明头部通过预处理器命令#include进行引用。
在C89标准中:
在94年的修正版中
<iso646.h>
<wchar.h>
<wctype.h>
在C99中增加了六个库
<complex.h>
<fenv.h>
<inttypes.h>
<stdbool.h>
<stdint.h>
<tgmath.h>
以上是C语言的标准。各个系统各自又对C库函数进行的各种扩充,就浩如烟海了。如POSIX C、GNU C等。关于(曾经)常用的C标准函数库,可参考《libc、glib、glibc简介》。
保留关键字
以下是C语言的保留关键字:
C99新增关键字
C11新增关键字
C标准的差异与特点
在C语言的发展历程中,C89(也称为ANSI C或C90标准)、C99和C11是三个重要的标准化里程碑。这些标准不仅为C语言定义了语法规则和库函数,还不断地进行更新和优化,以适应不断发展的编程需求。在此简单探讨C89、C99和C11这三个标准之间的主要区别,并突出它们各自的特点。
1990年,ANSI C 就被国际标准化组织 ISO 给采纳了。此时,C语言在 ISO 中有了一个官方名称——ISO/IEC 9899: 1990。其中 9899 是C语言在 ISO 标准中的代号,像 C++ 在 ISO 标准中的代号是 14882;而冒号后面的 1990 表示当前修订好的版本是在 1990 年发布的。
对于ISO/IEC 9899: 1990 的俗称或简称,有些地方称为 C89,有些地方称为 C90,或者 C89/90。不管怎么称呼,它们都指代这个最初的C语言国际标准。
C99是1999年颁布的标准,与C89/C90相比,它增加了许多新特性,如可变长数组、指向函数的指针、内嵌式文档、新的数学函数库等。
C11是2011年颁布的标准,它对C99的一些特性进行了改进,并增加了一些新特性,如线程安全函数、动态内存分配的安全性增强、对Unicode字符的支持等。
C17标准于2018年发布,是C语言的最新版本。C17主要是对C11标准的修订和更新,旨在进一步改进语言的特性和可用性。C17引入了一些新特性,如初始化宏、属性和线程局部存储等。
1、C89标准
C89标准是C语言的第一个官方标准,由美国国家标准协会(ANSI)在1989年发布。这个标准主要规定了C语言的基本语法、数据类型、运算符、控制结构、函数以及标准库等内容。该标准的核心目标是确保C语言在各种平台和编译器上的可移植性。关键特性:
定义了基本的C语言语法和语义。
引入了32个关键字,包括int, char, float, double, if, else, while, for, return等。
支持基本数据类型如整型、浮点型和字符型,并允许通过struct、union和enum等构造复杂数据类型。
提供了标准输入输出库函数,如printf和scanf。
2、C99标准
C99标准是在1999年由国际标准化组织(ISO)发布的ISO/IEC 9899: 1999,它在C89的基础上进行了许多改进和新增功能,使得C语言更加灵活和强大。与C89的主要区别及新增功能:
引入了单行注释//,使得代码注释更为便捷。
增加了long long int和_Complex、_Imaginary等新的数据类型。
提供了对变长数组的支持,允许在函数内部定义长度可变的数组。
引入了头文件,定义了布尔类型和true、false常量。
增加了%a和%A格式化输出,用于浮点数的十六进制表示。
允许在for循环初始化中声明变量,例如for(int i = 0; i < n; i++)。
C99 标准引入了许多特性,包括内联函数(inline functions)、可变长度的数组、灵活的数组成员(用于结构体)、复合字面量、指定成员的初始化器、对IEEE754浮点数的改进、支持不定参数个数的宏定义,在数据类型上还增加了 long long int 以及复数类型。毫不夸张地说,即便到目前为止,很少有C语言编译器是完整支持 C99 的。像主流的 GCC 以及 Clang 编译器都能支持高达90%以上,而微软的 Visual Studio 2015 中的C编译器只能支持到 70% 左右。
3、C11标准
2007 年,C语言标准委员会又重新开始修订C语言,到了 2011 年由ISO正式发布了 ISO/IEC 9899: 2011,简称为 C11 标准。
C11标准新引入的特征尽管没 C99 相对 C90 引入的那么多,但是这些也都十分有用,比如:字节对齐说明符、泛型机制(generic selection)、对多线程的支持、静态断言、原子操作以及对 Unicode 的支持。
C11在C99的基础上进一步增强了C语言的表达能力和安全性。与C99的主要区别及新增功能:
引入了_Generic关键字,允许根据表达式的类型选择不同的代码分支。
增加了_Static_assert宏,用于在编译时进行断言检查。
提供了_Alignof和_Alignas用于更精细的内存对齐控制。
引入了noreturn函数属性,用于指明函数不会返回。
增加了对多线程的支持,包括_Thread_local存储期说明符和头文件。
允许匿名结构体和联合体。
新的标准提高了对C++的兼容性,并增加了一些新的特性。这些新特性包括:
对齐处理(Alignment)的标准化(包括_Alignas标志符,alignof运算符, aligned_alloc函数以及<stdalign.h>头文件。
_Noreturn 函数标记,类似于 gcc 的 __attribute__((noreturn))。
_Generic 关键字。
多线程(Multithreading)支持,包括:_Thread_local存储类型标识符,<threads.h>头文件,里面包含了线程的创建和管理函数。
_Atomic类型修饰符和<stdatomic.h>头文件。
增强的Unicode的支持。基于C Unicode技术报告ISO/IEC TR 19769:2004,增强了对Unicode的支持。包括为UTF-16/UTF-32编码增加了char16_t和char32_t数据类型,提供了包含unicode字符串转换函数的头文件<uchar.h>。
删除了 gets() 函数,使用一个新的更安全的函数gets_s()替代。
增加了边界检查函数接口,定义了新的安全的函数,例如 fopen_s(),strcat_s() 等等。
增加了更多浮点处理宏。
匿名结构体/联合体支持。这个在gcc早已存在,C11将其引入标准。
静态断言(static assertions),_Static_assert(),在解释 #if 和 #error 之后被处理。
新的 fopen() 模式,(“…x”)。类似 POSIX 中的 O_CREAT|O_EXCL,在文件锁中比较常用。
新增 quick_exit() 函数作为第三种终止程序的方式。当 exit()失败时可以做最少的清理工作。
C89、C99和C11这三个标准体现了C语言从诞生到成熟的发展历程。每个标准都在前一个标准的基础上进行了改进和扩展,使得C语言更加适应现代编程的需求。了解这些标准之间的差异和特点对于编写兼容性和可移植性强的C代码至关重要。
C语言开发环境
在 Linux 下仍可以使用带界面的、便捷式的 IDE,例如 CodeLite、CLion(收费)、Code::Blocks、Netbeans、Eclipse CDT 等。微软的 Visual Studio 也支持 Linux ,在 Windows 下用惯了 Visual Studio 的用户也可以毫无障碍地迁移到 Linux 平台。但作为初学者,建议大家在命令行模式下编译C语言程序,这样能够了解生成一个可执行程序的详细步骤,以及编译命令的各种设置选项,为以后的 Linux C/C++ 开发打好坚实的基础。Linux 下使用最广泛的 C/C++ 编译器是 GCC,大多数的 Linux 发行版本都默认安装,不管是开发人员还是初学者,一般都将 GCC 作为 Linux 下首选的编译工具。GCC 仅仅是一个编译器,没有界面,必须在命令行模式下使用,通过gcc命令就可以将源文件编译成可执行文件。
分步骤编译
通过gcc命令一次性完成编译和链接的整个过程,这样最方便,在学习C语言的过程中一般都这么做。实际上gcc命令也可以将编译和链接分开,每次只完成一项任务;其编译其实是四个过程的集合,分别是预处理(preprocessing)、编译(compilation)、汇编(assembly)、链接(linking),分别由 cpp、cc1、as、ld 这四个程序完成,gcc 是它们的封装。这四个过程分别完成:处理 # 开头的预编译指令、将源码编译为汇编代码、将汇编代码编译为二进制代码、组合众多二进制代码生成可执行文件,也可分别调用 gcc -E、gcc -S、gcc -c、gcc 来完成。在这一过程中,文件经历了如下变化:main.c 到 main.i 到 main.s 到 main.o 到 main。
1)编译(Compile)
将源文件编译成目标文件需要使用-c选项,例如:gcc -c main.c
就将 main.c 编译为 main.o。打开 demo 目录,就会看到 main.o:
对于微软编译器(内嵌在 Visual C++ 或者 Visual Studio 中),目标文件的后缀为.obj;对于 GCC 编译器,目标文件的后缀为.o。
一个源文件会生成一个目标文件,多个源文件会生成多个目标文件,源文件数目和目标文件数目是一样的。通常情况下,默认的目标文件名字和源文件名字是一样的。
如果希望自定义目标文件的名字,那么可以使用-o选项,例如:gcc -c main.c -o a.o
这样生成的目标文件的名字就是 a.o。
2)链接(Link)
在gcc命令后面紧跟目标文件的名字,就可以将目标文件链接成为可执行文件,例如:
gcc main.o
就将 main.o 链接为 a.out。打开 demo 目录,就会看到 a.out。
在gcc命令后面紧跟源文件名字或者目标文件名字都是可以的,gcc命令能够自动识别到底是源文件还是目标文件:如果是源文件,那么要经过编译和链接两个步骤才能生成可执行文件;如果是目标文件,只需要链接就可以了。
使用-o选项仍然能够自定义可执行文件的名字,例如:gcc main.o -o freeoa.out
这样生成的可执行文件的名字就是 freeoa.out。
源代码经过编译(Compile)后就变成了可执行文件,其实这种说法有点笼统,甚至从严格意义上来讲是错误的。源代码要经过编译(Compile)和链接(Link)两个过程才能变成可执行文件。
编译器一次只能编译一个源文件,如果当前程序包含了多个源文件,那么就需要编译多次。编译器每次编译的结果是产生一个中间文件(可以认为是一种临时文件),而不是最终的可执行文件。中间文件已经非常接近可执行文件了,它们都是二进制格式,内部结构也非常相似。
将当前程序的所有中间文件以及系统库(暂时可以理解为系统中的一些组件)组合在一起,才能形成最终的可执行文件,这个组合的过程就叫做链接(Link)。完成链接功能的软件叫做链接器(Linker)。如果程序只包含了一个源文件,是不是就不需要链接了呢?不是的!
经过编译后程序虽然只有一个中间文件,不再需要和其它的中间文件组合了,但是这个唯一的中间文件还需要和系统库组合,这个过程也是链接。也就是说,不管有多少个源文件,都必须经过编译和链接两个过程才能生成可执行文件。
学习C语言用的软件有Turbo C、win-tc、GCC、Dev C++、VC++ 6.0、Code::Blocks、C-Free等等,有的是集成开发环境,有的是编译器,都是学习c语言人员需要了解和掌握的:
1、编译器:Turbo C、VC++ 6.0、GCC等,是容易上手、体积小、界面可视化的编译器。
2、win-tc、Dev C++、Code::Blocks、C-Free等,也是非常好用的集成开发环境,有的环境开放源码、功能齐全。
1、学习C语言常用的编译器软件
(1)Turbo C
在C语言学习工程中,Turbo C是最为经典的编译器,对于新手来说,比较容易上手,并且简单易学,系统体积也比较小,运行比较快,是学习C语言的首选软件。
(2)VC++ 6.0
VC++ 6.0是学习C语言较为主流的编译器,编程界面是可视化的,并且类和MFC非常强大,在编译过程中,如果出现错误,还会提示报错,帮助及时改正。
(3)GCC
GCC属于GNU编译器的套件,可以说是一款专用编译器,其中涉及libgcj语言库、C、C++、Java等语言的前端,可操作性强,c语言初学者可以选择性学习。
2、学习C语言常用开发环境
1).win-tc
在C语言学习过程中,使用win-tc开发环境是非常方便的,支持Windows系统,能帮助学习者用简单的方式进行计算机语言编译,还能进行低级存储器处理等,应用较为广泛。
2).Dev C++
Dev C++属于轻量级的C语言集成开发环境,适用于所有Windows系统,是一款自由软件,综合了编译器、调试器、格式整理器等众多软件功能,功能强大。
3).Code::Blocks
其属于功能全面、开放源码的C语言集成开发环境,有专业的图形界面库、运行迅速,使用的都是C++语言语言,有着较好的跨平台特点,初学者可以好好选择。
4).C-Free
C-Free是学习C语言的集成开发环境软件之一,非常专业,支持多种编译器,对于初学者来说,能快速实现编译、运行、连接和调试等等,安装方便、体积小,使用广泛。
以上这些都是学习C语言比较常用的软件,当然还有不少比较好的软件,如Visual Studio、Borland C++、WaTCom C++、GNU DJGPP C++、High C、MyTc、souceinsight等,对于初学者来说,还是要选择简单易学的。
学习C语言开发的几个IDE推荐
1).Visual Studio Code
Visual Studio Code是一款流行的免费开源IDE。它具有用户友好的界面,并支持广泛的编程语言,包括C语言。它提供了一系列功能,如智能代码补全、代码折叠和调试工具。该平台还支持扩展,允许开发人员添加功能和功能到IDE中。Visual Studio Code还有一个庞大的社区和一系列在线资源,可以帮助您开始C编程。
优点
免费和开源、用户友好的界面
提供一系列功能,如智能代码补全、代码折叠和调试工具
支持广泛的编程语言,包括C语言
有一个庞大的社区和一系列在线资源
缺点:对于较大的项目可能会很慢
2).Eclipse
Eclipse是另一款流行的C编程IDE。它是免费和开源的,为开发人员提供了一系列功能。Eclipse具有强大的代码编辑器、代码补全和调试工具。它还支持一系列插件,允许开发人员向IDE添加功能和功能。Eclipse高度可定制,开发人员可以从一系列主题和插件中选择,以增强他们的工作流程。Eclipse还有一个庞大的社区和一系列在线资源,可以帮助您开始C编程。
优点
免费和开源、强大的代码编辑器
提供一系列功能,如代码补全和调试工具
可高度定制,具有一系列主题和插件
支持广泛的编程语言,包括C语言
有一个庞大的社区和一系列在线资源
缺点:可能会占用较多的系统资源、学习曲线较陡峭
3).Code::Blocks
Code::Blocks是一款免费开源的IDE,专门为C编程而设计。它提供了一系列功能,如语法高亮、代码补全和调试工具。Code::Blocks还支持一系列编译器,并提供可定制的界面。Code::Blocks高度可定制,开发人员可以从一系列插件和主题中选择,以增强他们的工作流程。Code::Blocks还有一个庞大的社区和一系列在线资源,可以帮助您开始C编程。
优点
免费和开源、专门为C编程而设计、支持一系列编译器
提供一系列功能,如语法高亮和调试工具
可高度定制,具有一系列插件和主题
有一个庞大的社区和一系列在线资源
缺点:可能存在稳定性问题
4).NetBeans
NetBeans是一款流行的IDE,支持广泛的编程语言,包括C语言。它提供了一系列功能,如代码补全、调试工具和项目管理工具。NetBeans还支持一系列插件,允许开发人员向IDE添加功能和功能。NetBeans高度可定制,具有一系列主题和插件,开发人员可以用来增强他们的工作流程。NetBeans还有一个庞大的社区和一系列在线资源,可以帮助您开始C编程。优点
免费和开源
支持广泛的编程语言,包括C语言
提供一系列功能,如代码补全和调试工具
可高度定制,具有一系列主题和插件
有一个庞大的社区和一系列在线资源
缺点:对于较大的项目可能会很慢
5).Dev-C++
Dev-C++是一款免费开源的IDE,专门为C编程而设计。它提供了一系列功能,如语法高亮、代码补全和调试工具。Dev-C++还支持一系列编译器,并提供可定制的界面。Dev-C++高度可定制,开发人员可以从一系列插件和主题中选择,以增强他们的工作流程。Dev-C++还有一个庞大的社区和一系列在线资源,可以帮助您开始C编程。
优点
免费和开源、专门为C编程而设计、支持一系列编译器
提供一系列功能,如语法高亮和调试工具
可高度定制,具有一系列插件和主题
有一个庞大的社区和一系列在线资源
缺点:已有一段时间没有更新,可能与新系统不兼容
选择正确的IDE对于C编程至关重要。上述提到的IDE都是市场上较好的,为开发人员提供了一系列功能和功能。根据需求和偏好来选择最适合您的那个。
C编程语言已经存在了40多年,今天仍然广泛使用。它以其系统编程和高性能计算而闻名。C编程语言在各种应用程序中使用,包括操作系统、嵌入式系统和游戏引擎。然而,C编程语言对于初学者来说可能很难掌握。这就是为什么选择正确的IDE对于C编程至关重要。集成开发环境(IDE)是一种软件应用程序,为开发人员提供一系列工具和功能,以编写、测试和调试代码。IDE通过提供智能代码补全、语法高亮、调试工具和项目管理工具等功能,使编码更加高效和生产力。IDE还允许开发人员以更有组织的方式编写代码,使其更易于阅读和维护。
选择正确的IDE可能是一项有挑战的任务,特别是对于初学者来说。除了上述IDE之外,还有几个其他IDE可以用于C编程。其中一些IDE包括CodeLite、Geany和Sublime Text。这些IDE也受到开发人员的欢迎,并提供一系列功能和功能。C编程语言是一种强大的语言,需要一个好的IDE来充分发挥它的作用。上述提到的IDE都是市场上最好的,为开发人员提供了一系列功能和功能。根据您的需求和偏好,您可以选择最适合您的那个。在oschina上还有更详细的工具介绍:15 款最好的 C/C++ 编译器和集成开发环境
嵌入式开发所使用的编译器盘点
对嵌入式工程师来说,编译器就是不可或缺的神兵利器。由于C语言历史悠久,早期没有规范,整个计算机产业也都处于拓荒的年代,所以就涌现了很多款C语言编译器。它们皆可认为是C语言的翻译官。
所谓编译器,从概念上讲就是将“一种语言(通常为高级语言)”翻译为“另一种语言(通常为低级语言)”的程序。其工作原理就是是首先对源代码进行词法分析,将源代码分解成一系列的单词和语法元素。然后,将这些单词和语法元素转换成一系列机器指令,这些指令可以被计算机理解并执行。最后编译程器将这些指令转换成可执行的机器代码,这样计算机就可以运行它们了。换言之,C语言和机器语言是两种完全不同的语言,我们输入电脑的代码和机器理解的代码不是一回事,所以编译器就是把我们输入到电脑的代码翻译成电脑能理解的代码。
对嵌入式工程师来说,C语言是最基础的一种编程语言,而且大多单片机也是以C为基础的。根据桌面端和嵌入式系统端,C语言编译器分为两种大类。其中桌面端根据当前主流桌面操作系统,分为Visual C++ Studio、GCC及LLVM Clang三大编译器;嵌入式系统端则分为很多流派,包括大名鼎鼎的Keil C51、Arduino板搭载的开发套件可用针对AVR微控制器的AVR GCC编译器;Arm公司的ADS(ARM Development Suite)、RVDS(RealView Development Suite)、DS-5 Studio等;TI的CSS(Code Composer Studio);ADI(Analog Devices,Inc.)的Visual DSP++ 编译器等。
Visual Studio同时也被网友戏称是宇宙第一编译器,它是由微软研发,从1995年至今已经近28年,支持多种编程语言,C#/VB、JavaScript、.NET、Node.js、Java、Python、C++等等,一直深受开发者的喜爱。几乎常用到的所有应用程序,它都可以开发出来。它跨平台语言,高度集成,具有友好的用户界面、强大的调试功能、丰富的扩展库等特点,同样可以帮助快速高效地开发C语言程序。
被网友誉为C/C++开发神器的CLion也是一款不错的编译器。它是一款专为C语言和C++ 设计的IDE,其最大的优点是跨平台,在Linux、Mac、Windows 上都可以运行。最新版本CLion 2023支持C++14(几乎完全支持)和C++17(初次支持),可以帮助用户更快、更有效地编写代码。通过Disassembly view(反汇编视图),即使没有源代码,用户也可以轻松调试代码。同时在Catch的帮助下,用户可以测试代码,尝试对Microsoft Visual C++编译器进行实验性支持。
GCC(GNU Compiler Collection,GNU编译器套件)是由GNU开发的编程语言编译器,其初衷是为GNU操作系统专门编写的一款编译器。GCC的外部接口长得像一个标准的Unix编译器。使用者在命令列下键入gcc之程序名,以及一些命令参数,以便决定每个输入档案使用的个别语言编译器,并为输出程序码使用适合此硬件平台的组合语言编译器,并且选择性地执行连接器以制造可执行的程序。几乎全部的GCC都由C写成,除了Ada前端大部分以Ada写成。该编译器在编译速度、代码生成质量、诊断能力等方面都具有的表现,因此被广泛应用于各种领域。GCC支持的主要处理器架构:ARM、x86、x86-64、MIPS、PowerPC等。
说到GCC就不得不提Clang,它具有编译速度快、内存占用小以及与GCC等其他优秀特性的兼容性,这些特点使得许多工具都选择使用它。Clang是一个C、C++、Objective-C和Objective-C++编程语言的编译器前端。它采用了底层虚拟机(LLVM)作为其后端,其目标是提供一个GCC的替代品。作者是克里斯·拉特纳(Chris Lattner),在苹果公司的赞助支持下进行开发,而源代码授权是使用类BSD的伊利诺伊大学厄巴纳-香槟分校开源码许可,Clang主要由C++编写。Clang本身性能优异,其生成的AST所耗用掉的内存仅仅是GCC的20%左右,经测试证明Clang编译Objective-C代码时速度为GCC的3倍,还能针对用户发生的编译错误准确地给出建议。
除了上面这些,在线编译器也广受欢迎。2010年Mbed团队发布了基于浏览器的IDE,这使得为基于Arm的微控制器编写软件变得更快,更容易。从那时起,在线编译器已被全球数十万开发人员使用,许多人也是在那里学会为微控制器编写C++的。2021年末,Mbed在线编译器退役,由Keil Studio Cloud接替,Keil Studio Cloud是基于Arm为Mbed Studio和Mbed Online Compiler开发的技术构建,旨在提供一个易于使用的快速原型开发环境,可用于IoT、ML和一般嵌入式开发。其可以说是是Mbed Studio的进化版本,使用相同的底层IDE框架和代码库,并适应了对CMSIS生态系统的额外支持,与其他Keil工具(如Keil MDK)一样。
关于C系编程语言的延伸及主流厂商编译器区别可以参考《C系编程语言盘点》。
C语言核心要点
库函数的使用:学习常用库函数的使用和实现,理解库函数的工作原理。
数据类型:深入理解基本数据类型和构造类型,“指针”没有谁能逃得过去。
关键字的使用和技巧:熟悉C语言的关键字,并学会灵活运用它们来编写高效的代码。
调试方法和常见错误处理:掌握基本的调试技巧,学会识别和处理常见的编程错误。
数据结构:理解和掌握基本的数据结构,如栈、队列、链表和树,学会如何在实际项目中应用它们。
内存管理:深入理解内存管理的基本原理和技术,学会编写内存安全的代码。
熟悉 Linux 环境下的开发工具和技术(即上文所述的开发环境),以下是需要掌握的核心要点:
操作系统安装和基本命令:学会安装 Linux 系统和使用常用的命令。
VI 编辑器和 GCC:掌握 VI 编辑器的使用和 GCC 的编译与连接技术。
Makefile 和 CMake 编写:学会编写 Makefile 和 CMake 文件,掌握如何通过他们,实现项目的构建、编译和运行,其中 CMake 因为跨平台这个特性,现在是越来越多的开源项目都在使用它,而不是直接写 Makefile。
gdb 调试工具和 shell 编程:学会使用 gdb 进行程序调试和编写基本的 shell 脚本。
企业C语言软件开发规范和标准:理解和掌握企业级C语言软件开发的规范和标准。
Linux系统编程
系统编程:深入理解信号、系统调用、管道、FIFO、消息队列、共享内存等核心概念。
文件IO编程:掌握文件描述符、文件读写接口、原子操作、阻塞与非阻塞IO等技术。
多任务和多进程编程:学习进程表示、用户表述、fork与vfork、异步与多线程概念、线程同步等技术。
网络编程:掌握网络基本概念、套接口编程、网络字节序、Client、Server 结构、UDP 编程等。
数据库:SQLite3 是一个非常受欢迎和常用的嵌入式数据库解决方案,它因其轻量级和高效性而被广泛采用。
GNU 创始人 Richard Stallman(RMS)过去一段时间一直在编写一份 GNU C 语言的学习手册,2022年9月他发布公告,正式公开了这一成果 —— GNU C Language Intro and Reference Manual(GNU C 语言介绍和参考手册),其中涵盖了 GNU 对 C 编程语言的扩展。本手册解释了在 GNU/Linux 系统和其他系统上与 GNU Compiler Collection (GCC) 一起使用的 C 语言,通常其称之为 GNU C。如果已经了解了 C 语言,则可以将它用作参考手册;如果只了解基本的编程概念,但对 C 语言一无所知,那么可以从头开始按顺序阅读本手册以学习 C 语言。
参考:
业界对C语言的相关评测
C语言之父和Linux之父谁更伟大
阮一峰发布的《C语言入门教程》
List of C-family programming languages
Richard Stallman发布的《GNU C 语言介绍和参考手册》
C语言具有高效、灵活、功能丰富、表达力强和较高的可移植性等特点,在程序设计中备受青睐,成为最近年使用最为广泛的编程语言。目前,C语言编译器普遍存在于各种不同的操作系统中,例如Microsoft Windows、MacOS、Linux、Unix等。C语言的设计影响了众多后来的编程语言,例如C++、Objective-C、Java、C#等。
二十世纪八十年代,为了避免各开发厂商用的C语言的语法产生差异,由美国国家标准局为C语言订定了一套完整的国际标准语法,称为ANSI C,作为C语言的标准。二十世纪八十年代至今的有关程序开发工具,一般都支持符合ANSI C的语法。
C之父,Dennis Ritchie
20世纪70年代,肯·汤普逊为了使其设计的Unix系统更加高效,使用B语言的变种(即C语言)重写了Unix。在1978年,丹尼斯·里奇和布莱恩·柯林汉合作出版了《C程序设计语言》第一版,事实上即为K&R C标准。在此之后,制定标准的工作转移到ISO和ANSI。目前最新的C语言标准是C18:ISO/IEC 9899:2018(2018年6月修订)。
特性
C语言是一个有结构化程序设计、具有变量作用域(variable scope)以及递归功能的过程式语言。
C语言传递参数均是以值传递(pass by value),另外也可以传递指针(a pointer passed by value)。
不同的变量类型可以用结构体(struct)组合在一起。
只有32个保留字(reserved keywords),使变量、函数命名有更多弹性。
部分的变量类型可以转换,例如整数型和字符型变量。
透过指针(pointer),C语言可以容易的对存储器进行低端控制。
编译预处理(preprocessor),让C语言的编译更具有弹性。
在C语言中,程序从 main 开始执行。main 函数通过调用和控制其他函数进行工作。例如printf,程序员可以自己写函数,或从库中调用函数。在'return 0;'中使得 main 返回一个值给调用程序的壳层,表明程序是否成功运行。
一个C语言的函数由返回值、函数名、参数列表和函数体组成,函数体的语法和其它的复合的语句部分是一样的。
C语言的特色之一是:语言不负责内存边界检查,这是因为在运行时进行内存边界检查会造成性能问题,与UNIX哲学不符。此特性容易导致缓冲区溢出问题。然而部分编译器(如英特尔编译器)会出于安全性的考量,提供方法以进行运行时内存边界检查。
复合语句
C语言中的复合语句(或称语句块)的格式为:
{
语句;
语句;
/* ... */
}
复合语句可以使得几个语句从文法上变成一个语句。有时必须使用复合语句,否则会产生错误。例如,在运用循环语句的时候,如果循环体(即循环中执行部分)包含多个语句(以分号隔开),则必须使用花括号将他们合并成一个复合语句。如果不这么做,系统仅把第一个分号前的内容看做循环体。
需要注意的是,部分C编译器并不支持在任意位置使用复合语句。
条件语句
C语言有两种条件语句形式,分别是if和switch。
If 的格式如下:
if (运算式) // 如果
语句;
// 有时还会有 else:
else // 否则
语句;
表达式的值非零表示条件为真;如果条件为假,程序将跳过if处的语句,直接运行if后面的语句。但是如果if后面有else,则当条件为假时,程序跳到else处运行。if和else后面的语句可以是另个if语句,这种套叠式的结构,允许更复杂的逻辑控制流程得以实现。在一般情况下,else一定与最接近的if成对,因此常用括弧{}越过此限制。比较下面两种情况:
if (逻辑表达)
if (逻辑表达式)
语句;
else
语句;
下面是if的另一种格式if……else if……else……
if (逻辑表达式)
语句;
else if (逻辑表达式) //在下面可以加入多个“else if()”语句
语句;
else
语句;
if (逻辑表达式甲) {
if (逻辑表达式乙)
语句;
}
else
语句;
要注意这里的缩进和换行只用于方便阅读。编译器并不会根据缩进层级猜测 if 和 else 的对应关系。
switch通常用于对几种有明确值的条件进行控制。它要求的条件值通常是整数或字符。与switch搭配的条件转移是case。使用case后面的标值,控制程序将跳到满足条件的case处一直往下运行,直到语句结束或遇到break。通常可以使用default把其他例外的情况包含进去。如果switch语句中的条件不成立,控制程序将跳到default处运行;如果省略default子句,则直接运行下一语句。switch是可以嵌套的。
switch (值) {
case 甲:
case 乙:
语句段1; // 甲乙都会执行
// 更多语句…
break; // 跳转到 switch 末尾处
case 丙:
语句段2;
break;
default: // 无论如何都会匹配
语句段3;
}
简单的条件判断也可用?:
运算式?值1:值2;
如:a=b>c?b:c // 如果变数"b"的值大于变数"c" 把变数"b"的值赋予变数"a"
循环语句
C语言有三种形式的循环语句:
do
语句
while(判断式);
和:
while(判断式)
语句;
和:
for(起始化;判断式;运算式)
语句;
在while和for中,语句将运行到表达式的值为零时结束。在do...while语句中,循环将至少被运行一次。这三种循环结构可以互相转化:
for(起始化;判断式;运算式)
语句;
如果语句中不使用continue语句的话,相当于
起始化;
while (判断式) {
语句;
运算式;
}
当循环条件一直为真时,将产生死循环。
跳转语句
跳转语句包括四种:goto,continue,break和return。
goto 标记;
goto语句是无条件转移语句,且标记必须在当前函数中定义,使用“标记:”的格式定义。程序将跳到标记处继续运行。由于goto(特别是向回 goto 和长距离的 goto)容易产生阅读上的困难,所以对新手应该尽量少用。GCC 编译器拓展支持对指针 goto和宏内 goto,一定程度上增强了 goto 的可读性。
continue语句用在循环语句中,作用是结束当前一轮的循环,马上开始下一轮循环。
break语句用在循环语句或switch中,作用是结束当前循环,跳到循环体外继续运行。但是使用break只能跳出一层循环。在要跳出多重循环时,可以使用goto使得程序更为简洁。
当一个函数运行结束后要返回一个值时,使用return,return可以跟一个表达式或变量。如果return后面没有值,将运行不返回值。
指针
如果一个变量声明时在前面使用 * 号,表明这是个指针型变量。换句话说,该变量存储一个地址,而 *(此处特指单目运算符 *,下同。C语言中另有双目运算符 * 表示乘) 则是取内容操作符,意思是取这个内存地址里存储的内容。把这两点结合在一起,可将 int *a;看作是 “*a 解得的内容类型为 int”,对更复杂的声明也如此。指针是 C 语言区别于其他同时代高级语言的主要特征之一。
指针不仅可以是变量的地址,还可以是数组、数组元素、函数的地址。通过指针作为形式参数可以在函数的调用过程得到一个以上的返回值(不同于return z这样的仅能得到一个返回值。
指针是一把双刃剑,许多操作可以通过指针自然的表达,但是不正确的或者过分的使用指针又会给程序带来大量潜在的错误。
例如:
int *pi; // 指向整型数据的指针 pi
int * api[3]; // 由指向整型数据的指针构成的数组,长度为 3
char ** argv; // 指向一个字符指针的指针
struct { int member; } stinst,
* pst = & stinst;
// pst是一个指向一个匿名结构体的指针
储存在指针中的地址所指向的数值在程序中可以由 * 读取。例如,在第一个例子中, *pi 是一个整型数据。这叫做引用一个指针。
另一个运算符 &,叫做取地址运算符,它将返回一个变量、数组或函数的存储地址。因此看下面的例子:
int i, *pi; /* int and pointer to int */
pi = &i;
i 和 *pi 在程序中可以相互替换使用,直到 pi 被改变成指向另一个变量的地址。
当指针指向结构体时,可以使用运算符 -> 代替 *和. 的作用,如 (*p).m 与 p->m 等效。
字符串
C语言的字符串其实就是char型数组,所以使用字符串并不需要引用库。然而C标准库确实包含了用于对字符串进行操作的函数,使得它们看起来就像字符串而不是数组。使用这些函数需要引用头文件string.h。
文件输入/输出
在C语言中,输入和输出是经由标准库中的一组函数来实现的。在ANSI/ISO C中,这些函数被定义在头文件stdio.h中。
标准输入/输出
有三个标准输入/输出是标准I/O库预先定义的:
stdin 标准输入
stdout 标准输出
stderr 输入输出错误
下面的这个例子显示了一个过滤程序(filter program)是怎样构成的。
#include <stdio.h>
int main(int argc, const char * argv[]){
char c;
while ((c=getchar())!=EOF)
putchar(c);
perror("getchar() got EOF");
return -1;
}
函数
C语言的基本结构单位是函数。系统首先调用 main函数(主函数),通过函数的嵌套调用,再调用其他函数。函数可以是系统自带的函数,也可以是用户定义的函数。C语言中,不允许函数嵌套定义。
内存管理
C语言的特色之一是:程序员必须亲自处理内存的分配细节。大多数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语言的标准文档要求了一个平台移植C语言的时候至少要实现的一些功能和封装的集合,称为“标准库”,标准库的声明头部通过预处理器命令#include进行引用。
在C89标准中:
文件 | 简介说明 |
---|---|
<assert.h> | 断言相关 |
<ctype.h> | 字符类型判断 |
<errno.h> | 标准报错机制 |
<float.h> | 浮点运算 |
<limits.h> | 各种体系结构限制 |
<locale.h> | 本地化接口 |
<math.h> | 数学函数 |
<setjmp.h> | 跨函数跳转 |
<signal.h> | 信号(类似UNIX的信号定义,但是差很远) |
<stdarg.h> | 可变参处理 |
<stddef.h> | 一些标准宏定义 |
<stdio.h> | 标准I/O库 |
<stdlib.h> | 标准工具库函数 |
<string.h> | ASCIIZ字符串及任意内存处理函数 |
<time.h> | 时间相关 |
在94年的修正版中
<iso646.h>
<wchar.h>
<wctype.h>
在C99中增加了六个库
<complex.h>
<fenv.h>
<inttypes.h>
<stdbool.h>
<stdint.h>
<tgmath.h>
以上是C语言的标准。各个系统各自又对C库函数进行的各种扩充,就浩如烟海了。如POSIX C、GNU C等。关于(曾经)常用的C标准函数库,可参考《libc、glib、glibc简介》。
保留关键字
以下是C语言的保留关键字:
char | short | int | unsigned |
long | float | double | struct |
union | void | enum | signed |
const | volatile | typedef | auto |
register | static | extern | break |
case | continue | default | do |
else | for | goto | if |
return | switch | while | sizeof |
C99新增关键字
_Bool | _Complex | _Imaginary | inline | restrict |
C11新增关键字
_Alignas | _Alignof | _Atomic | _Generic | _Noreturn |
_Static_assert | _Thread_local |
C标准的差异与特点
在C语言的发展历程中,C89(也称为ANSI C或C90标准)、C99和C11是三个重要的标准化里程碑。这些标准不仅为C语言定义了语法规则和库函数,还不断地进行更新和优化,以适应不断发展的编程需求。在此简单探讨C89、C99和C11这三个标准之间的主要区别,并突出它们各自的特点。
1990年,ANSI C 就被国际标准化组织 ISO 给采纳了。此时,C语言在 ISO 中有了一个官方名称——ISO/IEC 9899: 1990。其中 9899 是C语言在 ISO 标准中的代号,像 C++ 在 ISO 标准中的代号是 14882;而冒号后面的 1990 表示当前修订好的版本是在 1990 年发布的。
对于ISO/IEC 9899: 1990 的俗称或简称,有些地方称为 C89,有些地方称为 C90,或者 C89/90。不管怎么称呼,它们都指代这个最初的C语言国际标准。
C99是1999年颁布的标准,与C89/C90相比,它增加了许多新特性,如可变长数组、指向函数的指针、内嵌式文档、新的数学函数库等。
C11是2011年颁布的标准,它对C99的一些特性进行了改进,并增加了一些新特性,如线程安全函数、动态内存分配的安全性增强、对Unicode字符的支持等。
C17标准于2018年发布,是C语言的最新版本。C17主要是对C11标准的修订和更新,旨在进一步改进语言的特性和可用性。C17引入了一些新特性,如初始化宏、属性和线程局部存储等。
1、C89标准
C89标准是C语言的第一个官方标准,由美国国家标准协会(ANSI)在1989年发布。这个标准主要规定了C语言的基本语法、数据类型、运算符、控制结构、函数以及标准库等内容。该标准的核心目标是确保C语言在各种平台和编译器上的可移植性。关键特性:
定义了基本的C语言语法和语义。
引入了32个关键字,包括int, char, float, double, if, else, while, for, return等。
支持基本数据类型如整型、浮点型和字符型,并允许通过struct、union和enum等构造复杂数据类型。
提供了标准输入输出库函数,如printf和scanf。
2、C99标准
C99标准是在1999年由国际标准化组织(ISO)发布的ISO/IEC 9899: 1999,它在C89的基础上进行了许多改进和新增功能,使得C语言更加灵活和强大。与C89的主要区别及新增功能:
引入了单行注释//,使得代码注释更为便捷。
增加了long long int和_Complex、_Imaginary等新的数据类型。
提供了对变长数组的支持,允许在函数内部定义长度可变的数组。
引入了头文件,定义了布尔类型和true、false常量。
增加了%a和%A格式化输出,用于浮点数的十六进制表示。
允许在for循环初始化中声明变量,例如for(int i = 0; i < n; i++)。
C99 标准引入了许多特性,包括内联函数(inline functions)、可变长度的数组、灵活的数组成员(用于结构体)、复合字面量、指定成员的初始化器、对IEEE754浮点数的改进、支持不定参数个数的宏定义,在数据类型上还增加了 long long int 以及复数类型。毫不夸张地说,即便到目前为止,很少有C语言编译器是完整支持 C99 的。像主流的 GCC 以及 Clang 编译器都能支持高达90%以上,而微软的 Visual Studio 2015 中的C编译器只能支持到 70% 左右。
3、C11标准
2007 年,C语言标准委员会又重新开始修订C语言,到了 2011 年由ISO正式发布了 ISO/IEC 9899: 2011,简称为 C11 标准。
C11标准新引入的特征尽管没 C99 相对 C90 引入的那么多,但是这些也都十分有用,比如:字节对齐说明符、泛型机制(generic selection)、对多线程的支持、静态断言、原子操作以及对 Unicode 的支持。
C11在C99的基础上进一步增强了C语言的表达能力和安全性。与C99的主要区别及新增功能:
引入了_Generic关键字,允许根据表达式的类型选择不同的代码分支。
增加了_Static_assert宏,用于在编译时进行断言检查。
提供了_Alignof和_Alignas用于更精细的内存对齐控制。
引入了noreturn函数属性,用于指明函数不会返回。
增加了对多线程的支持,包括_Thread_local存储期说明符和头文件。
允许匿名结构体和联合体。
新的标准提高了对C++的兼容性,并增加了一些新的特性。这些新特性包括:
对齐处理(Alignment)的标准化(包括_Alignas标志符,alignof运算符, aligned_alloc函数以及<stdalign.h>头文件。
_Noreturn 函数标记,类似于 gcc 的 __attribute__((noreturn))。
_Generic 关键字。
多线程(Multithreading)支持,包括:_Thread_local存储类型标识符,<threads.h>头文件,里面包含了线程的创建和管理函数。
_Atomic类型修饰符和<stdatomic.h>头文件。
增强的Unicode的支持。基于C Unicode技术报告ISO/IEC TR 19769:2004,增强了对Unicode的支持。包括为UTF-16/UTF-32编码增加了char16_t和char32_t数据类型,提供了包含unicode字符串转换函数的头文件<uchar.h>。
删除了 gets() 函数,使用一个新的更安全的函数gets_s()替代。
增加了边界检查函数接口,定义了新的安全的函数,例如 fopen_s(),strcat_s() 等等。
增加了更多浮点处理宏。
匿名结构体/联合体支持。这个在gcc早已存在,C11将其引入标准。
静态断言(static assertions),_Static_assert(),在解释 #if 和 #error 之后被处理。
新的 fopen() 模式,(“…x”)。类似 POSIX 中的 O_CREAT|O_EXCL,在文件锁中比较常用。
新增 quick_exit() 函数作为第三种终止程序的方式。当 exit()失败时可以做最少的清理工作。
C89、C99和C11这三个标准体现了C语言从诞生到成熟的发展历程。每个标准都在前一个标准的基础上进行了改进和扩展,使得C语言更加适应现代编程的需求。了解这些标准之间的差异和特点对于编写兼容性和可移植性强的C代码至关重要。
C语言开发环境
在 Linux 下仍可以使用带界面的、便捷式的 IDE,例如 CodeLite、CLion(收费)、Code::Blocks、Netbeans、Eclipse CDT 等。微软的 Visual Studio 也支持 Linux ,在 Windows 下用惯了 Visual Studio 的用户也可以毫无障碍地迁移到 Linux 平台。但作为初学者,建议大家在命令行模式下编译C语言程序,这样能够了解生成一个可执行程序的详细步骤,以及编译命令的各种设置选项,为以后的 Linux C/C++ 开发打好坚实的基础。Linux 下使用最广泛的 C/C++ 编译器是 GCC,大多数的 Linux 发行版本都默认安装,不管是开发人员还是初学者,一般都将 GCC 作为 Linux 下首选的编译工具。GCC 仅仅是一个编译器,没有界面,必须在命令行模式下使用,通过gcc命令就可以将源文件编译成可执行文件。
分步骤编译
通过gcc命令一次性完成编译和链接的整个过程,这样最方便,在学习C语言的过程中一般都这么做。实际上gcc命令也可以将编译和链接分开,每次只完成一项任务;其编译其实是四个过程的集合,分别是预处理(preprocessing)、编译(compilation)、汇编(assembly)、链接(linking),分别由 cpp、cc1、as、ld 这四个程序完成,gcc 是它们的封装。这四个过程分别完成:处理 # 开头的预编译指令、将源码编译为汇编代码、将汇编代码编译为二进制代码、组合众多二进制代码生成可执行文件,也可分别调用 gcc -E、gcc -S、gcc -c、gcc 来完成。在这一过程中,文件经历了如下变化:main.c 到 main.i 到 main.s 到 main.o 到 main。
1)编译(Compile)
将源文件编译成目标文件需要使用-c选项,例如:gcc -c main.c
就将 main.c 编译为 main.o。打开 demo 目录,就会看到 main.o:
对于微软编译器(内嵌在 Visual C++ 或者 Visual Studio 中),目标文件的后缀为.obj;对于 GCC 编译器,目标文件的后缀为.o。
一个源文件会生成一个目标文件,多个源文件会生成多个目标文件,源文件数目和目标文件数目是一样的。通常情况下,默认的目标文件名字和源文件名字是一样的。
如果希望自定义目标文件的名字,那么可以使用-o选项,例如:gcc -c main.c -o a.o
这样生成的目标文件的名字就是 a.o。
2)链接(Link)
在gcc命令后面紧跟目标文件的名字,就可以将目标文件链接成为可执行文件,例如:
gcc main.o
就将 main.o 链接为 a.out。打开 demo 目录,就会看到 a.out。
在gcc命令后面紧跟源文件名字或者目标文件名字都是可以的,gcc命令能够自动识别到底是源文件还是目标文件:如果是源文件,那么要经过编译和链接两个步骤才能生成可执行文件;如果是目标文件,只需要链接就可以了。
使用-o选项仍然能够自定义可执行文件的名字,例如:gcc main.o -o freeoa.out
这样生成的可执行文件的名字就是 freeoa.out。
源代码经过编译(Compile)后就变成了可执行文件,其实这种说法有点笼统,甚至从严格意义上来讲是错误的。源代码要经过编译(Compile)和链接(Link)两个过程才能变成可执行文件。
编译器一次只能编译一个源文件,如果当前程序包含了多个源文件,那么就需要编译多次。编译器每次编译的结果是产生一个中间文件(可以认为是一种临时文件),而不是最终的可执行文件。中间文件已经非常接近可执行文件了,它们都是二进制格式,内部结构也非常相似。
将当前程序的所有中间文件以及系统库(暂时可以理解为系统中的一些组件)组合在一起,才能形成最终的可执行文件,这个组合的过程就叫做链接(Link)。完成链接功能的软件叫做链接器(Linker)。如果程序只包含了一个源文件,是不是就不需要链接了呢?不是的!
经过编译后程序虽然只有一个中间文件,不再需要和其它的中间文件组合了,但是这个唯一的中间文件还需要和系统库组合,这个过程也是链接。也就是说,不管有多少个源文件,都必须经过编译和链接两个过程才能生成可执行文件。
学习C语言用的软件有Turbo C、win-tc、GCC、Dev C++、VC++ 6.0、Code::Blocks、C-Free等等,有的是集成开发环境,有的是编译器,都是学习c语言人员需要了解和掌握的:
1、编译器:Turbo C、VC++ 6.0、GCC等,是容易上手、体积小、界面可视化的编译器。
2、win-tc、Dev C++、Code::Blocks、C-Free等,也是非常好用的集成开发环境,有的环境开放源码、功能齐全。
1、学习C语言常用的编译器软件
(1)Turbo C
在C语言学习工程中,Turbo C是最为经典的编译器,对于新手来说,比较容易上手,并且简单易学,系统体积也比较小,运行比较快,是学习C语言的首选软件。
(2)VC++ 6.0
VC++ 6.0是学习C语言较为主流的编译器,编程界面是可视化的,并且类和MFC非常强大,在编译过程中,如果出现错误,还会提示报错,帮助及时改正。
(3)GCC
GCC属于GNU编译器的套件,可以说是一款专用编译器,其中涉及libgcj语言库、C、C++、Java等语言的前端,可操作性强,c语言初学者可以选择性学习。
2、学习C语言常用开发环境
1).win-tc
在C语言学习过程中,使用win-tc开发环境是非常方便的,支持Windows系统,能帮助学习者用简单的方式进行计算机语言编译,还能进行低级存储器处理等,应用较为广泛。
2).Dev C++
Dev C++属于轻量级的C语言集成开发环境,适用于所有Windows系统,是一款自由软件,综合了编译器、调试器、格式整理器等众多软件功能,功能强大。
3).Code::Blocks
其属于功能全面、开放源码的C语言集成开发环境,有专业的图形界面库、运行迅速,使用的都是C++语言语言,有着较好的跨平台特点,初学者可以好好选择。
4).C-Free
C-Free是学习C语言的集成开发环境软件之一,非常专业,支持多种编译器,对于初学者来说,能快速实现编译、运行、连接和调试等等,安装方便、体积小,使用广泛。
以上这些都是学习C语言比较常用的软件,当然还有不少比较好的软件,如Visual Studio、Borland C++、WaTCom C++、GNU DJGPP C++、High C、MyTc、souceinsight等,对于初学者来说,还是要选择简单易学的。
学习C语言开发的几个IDE推荐
1).Visual Studio Code
Visual Studio Code是一款流行的免费开源IDE。它具有用户友好的界面,并支持广泛的编程语言,包括C语言。它提供了一系列功能,如智能代码补全、代码折叠和调试工具。该平台还支持扩展,允许开发人员添加功能和功能到IDE中。Visual Studio Code还有一个庞大的社区和一系列在线资源,可以帮助您开始C编程。
优点
免费和开源、用户友好的界面
提供一系列功能,如智能代码补全、代码折叠和调试工具
支持广泛的编程语言,包括C语言
有一个庞大的社区和一系列在线资源
缺点:对于较大的项目可能会很慢
2).Eclipse
Eclipse是另一款流行的C编程IDE。它是免费和开源的,为开发人员提供了一系列功能。Eclipse具有强大的代码编辑器、代码补全和调试工具。它还支持一系列插件,允许开发人员向IDE添加功能和功能。Eclipse高度可定制,开发人员可以从一系列主题和插件中选择,以增强他们的工作流程。Eclipse还有一个庞大的社区和一系列在线资源,可以帮助您开始C编程。
优点
免费和开源、强大的代码编辑器
提供一系列功能,如代码补全和调试工具
可高度定制,具有一系列主题和插件
支持广泛的编程语言,包括C语言
有一个庞大的社区和一系列在线资源
缺点:可能会占用较多的系统资源、学习曲线较陡峭
3).Code::Blocks
Code::Blocks是一款免费开源的IDE,专门为C编程而设计。它提供了一系列功能,如语法高亮、代码补全和调试工具。Code::Blocks还支持一系列编译器,并提供可定制的界面。Code::Blocks高度可定制,开发人员可以从一系列插件和主题中选择,以增强他们的工作流程。Code::Blocks还有一个庞大的社区和一系列在线资源,可以帮助您开始C编程。
优点
免费和开源、专门为C编程而设计、支持一系列编译器
提供一系列功能,如语法高亮和调试工具
可高度定制,具有一系列插件和主题
有一个庞大的社区和一系列在线资源
缺点:可能存在稳定性问题
4).NetBeans
NetBeans是一款流行的IDE,支持广泛的编程语言,包括C语言。它提供了一系列功能,如代码补全、调试工具和项目管理工具。NetBeans还支持一系列插件,允许开发人员向IDE添加功能和功能。NetBeans高度可定制,具有一系列主题和插件,开发人员可以用来增强他们的工作流程。NetBeans还有一个庞大的社区和一系列在线资源,可以帮助您开始C编程。优点
免费和开源
支持广泛的编程语言,包括C语言
提供一系列功能,如代码补全和调试工具
可高度定制,具有一系列主题和插件
有一个庞大的社区和一系列在线资源
缺点:对于较大的项目可能会很慢
5).Dev-C++
Dev-C++是一款免费开源的IDE,专门为C编程而设计。它提供了一系列功能,如语法高亮、代码补全和调试工具。Dev-C++还支持一系列编译器,并提供可定制的界面。Dev-C++高度可定制,开发人员可以从一系列插件和主题中选择,以增强他们的工作流程。Dev-C++还有一个庞大的社区和一系列在线资源,可以帮助您开始C编程。
优点
免费和开源、专门为C编程而设计、支持一系列编译器
提供一系列功能,如语法高亮和调试工具
可高度定制,具有一系列插件和主题
有一个庞大的社区和一系列在线资源
缺点:已有一段时间没有更新,可能与新系统不兼容
选择正确的IDE对于C编程至关重要。上述提到的IDE都是市场上较好的,为开发人员提供了一系列功能和功能。根据需求和偏好来选择最适合您的那个。
C编程语言已经存在了40多年,今天仍然广泛使用。它以其系统编程和高性能计算而闻名。C编程语言在各种应用程序中使用,包括操作系统、嵌入式系统和游戏引擎。然而,C编程语言对于初学者来说可能很难掌握。这就是为什么选择正确的IDE对于C编程至关重要。集成开发环境(IDE)是一种软件应用程序,为开发人员提供一系列工具和功能,以编写、测试和调试代码。IDE通过提供智能代码补全、语法高亮、调试工具和项目管理工具等功能,使编码更加高效和生产力。IDE还允许开发人员以更有组织的方式编写代码,使其更易于阅读和维护。
选择正确的IDE可能是一项有挑战的任务,特别是对于初学者来说。除了上述IDE之外,还有几个其他IDE可以用于C编程。其中一些IDE包括CodeLite、Geany和Sublime Text。这些IDE也受到开发人员的欢迎,并提供一系列功能和功能。C编程语言是一种强大的语言,需要一个好的IDE来充分发挥它的作用。上述提到的IDE都是市场上最好的,为开发人员提供了一系列功能和功能。根据您的需求和偏好,您可以选择最适合您的那个。在oschina上还有更详细的工具介绍:15 款最好的 C/C++ 编译器和集成开发环境
嵌入式开发所使用的编译器盘点
对嵌入式工程师来说,编译器就是不可或缺的神兵利器。由于C语言历史悠久,早期没有规范,整个计算机产业也都处于拓荒的年代,所以就涌现了很多款C语言编译器。它们皆可认为是C语言的翻译官。
所谓编译器,从概念上讲就是将“一种语言(通常为高级语言)”翻译为“另一种语言(通常为低级语言)”的程序。其工作原理就是是首先对源代码进行词法分析,将源代码分解成一系列的单词和语法元素。然后,将这些单词和语法元素转换成一系列机器指令,这些指令可以被计算机理解并执行。最后编译程器将这些指令转换成可执行的机器代码,这样计算机就可以运行它们了。换言之,C语言和机器语言是两种完全不同的语言,我们输入电脑的代码和机器理解的代码不是一回事,所以编译器就是把我们输入到电脑的代码翻译成电脑能理解的代码。
对嵌入式工程师来说,C语言是最基础的一种编程语言,而且大多单片机也是以C为基础的。根据桌面端和嵌入式系统端,C语言编译器分为两种大类。其中桌面端根据当前主流桌面操作系统,分为Visual C++ Studio、GCC及LLVM Clang三大编译器;嵌入式系统端则分为很多流派,包括大名鼎鼎的Keil C51、Arduino板搭载的开发套件可用针对AVR微控制器的AVR GCC编译器;Arm公司的ADS(ARM Development Suite)、RVDS(RealView Development Suite)、DS-5 Studio等;TI的CSS(Code Composer Studio);ADI(Analog Devices,Inc.)的Visual DSP++ 编译器等。
Visual Studio同时也被网友戏称是宇宙第一编译器,它是由微软研发,从1995年至今已经近28年,支持多种编程语言,C#/VB、JavaScript、.NET、Node.js、Java、Python、C++等等,一直深受开发者的喜爱。几乎常用到的所有应用程序,它都可以开发出来。它跨平台语言,高度集成,具有友好的用户界面、强大的调试功能、丰富的扩展库等特点,同样可以帮助快速高效地开发C语言程序。
被网友誉为C/C++开发神器的CLion也是一款不错的编译器。它是一款专为C语言和C++ 设计的IDE,其最大的优点是跨平台,在Linux、Mac、Windows 上都可以运行。最新版本CLion 2023支持C++14(几乎完全支持)和C++17(初次支持),可以帮助用户更快、更有效地编写代码。通过Disassembly view(反汇编视图),即使没有源代码,用户也可以轻松调试代码。同时在Catch的帮助下,用户可以测试代码,尝试对Microsoft Visual C++编译器进行实验性支持。
GCC(GNU Compiler Collection,GNU编译器套件)是由GNU开发的编程语言编译器,其初衷是为GNU操作系统专门编写的一款编译器。GCC的外部接口长得像一个标准的Unix编译器。使用者在命令列下键入gcc之程序名,以及一些命令参数,以便决定每个输入档案使用的个别语言编译器,并为输出程序码使用适合此硬件平台的组合语言编译器,并且选择性地执行连接器以制造可执行的程序。几乎全部的GCC都由C写成,除了Ada前端大部分以Ada写成。该编译器在编译速度、代码生成质量、诊断能力等方面都具有的表现,因此被广泛应用于各种领域。GCC支持的主要处理器架构:ARM、x86、x86-64、MIPS、PowerPC等。
说到GCC就不得不提Clang,它具有编译速度快、内存占用小以及与GCC等其他优秀特性的兼容性,这些特点使得许多工具都选择使用它。Clang是一个C、C++、Objective-C和Objective-C++编程语言的编译器前端。它采用了底层虚拟机(LLVM)作为其后端,其目标是提供一个GCC的替代品。作者是克里斯·拉特纳(Chris Lattner),在苹果公司的赞助支持下进行开发,而源代码授权是使用类BSD的伊利诺伊大学厄巴纳-香槟分校开源码许可,Clang主要由C++编写。Clang本身性能优异,其生成的AST所耗用掉的内存仅仅是GCC的20%左右,经测试证明Clang编译Objective-C代码时速度为GCC的3倍,还能针对用户发生的编译错误准确地给出建议。
除了上面这些,在线编译器也广受欢迎。2010年Mbed团队发布了基于浏览器的IDE,这使得为基于Arm的微控制器编写软件变得更快,更容易。从那时起,在线编译器已被全球数十万开发人员使用,许多人也是在那里学会为微控制器编写C++的。2021年末,Mbed在线编译器退役,由Keil Studio Cloud接替,Keil Studio Cloud是基于Arm为Mbed Studio和Mbed Online Compiler开发的技术构建,旨在提供一个易于使用的快速原型开发环境,可用于IoT、ML和一般嵌入式开发。其可以说是是Mbed Studio的进化版本,使用相同的底层IDE框架和代码库,并适应了对CMSIS生态系统的额外支持,与其他Keil工具(如Keil MDK)一样。
关于C系编程语言的延伸及主流厂商编译器区别可以参考《C系编程语言盘点》。
C语言核心要点
库函数的使用:学习常用库函数的使用和实现,理解库函数的工作原理。
数据类型:深入理解基本数据类型和构造类型,“指针”没有谁能逃得过去。
关键字的使用和技巧:熟悉C语言的关键字,并学会灵活运用它们来编写高效的代码。
调试方法和常见错误处理:掌握基本的调试技巧,学会识别和处理常见的编程错误。
数据结构:理解和掌握基本的数据结构,如栈、队列、链表和树,学会如何在实际项目中应用它们。
内存管理:深入理解内存管理的基本原理和技术,学会编写内存安全的代码。
熟悉 Linux 环境下的开发工具和技术(即上文所述的开发环境),以下是需要掌握的核心要点:
操作系统安装和基本命令:学会安装 Linux 系统和使用常用的命令。
VI 编辑器和 GCC:掌握 VI 编辑器的使用和 GCC 的编译与连接技术。
Makefile 和 CMake 编写:学会编写 Makefile 和 CMake 文件,掌握如何通过他们,实现项目的构建、编译和运行,其中 CMake 因为跨平台这个特性,现在是越来越多的开源项目都在使用它,而不是直接写 Makefile。
gdb 调试工具和 shell 编程:学会使用 gdb 进行程序调试和编写基本的 shell 脚本。
企业C语言软件开发规范和标准:理解和掌握企业级C语言软件开发的规范和标准。
Linux系统编程
系统编程:深入理解信号、系统调用、管道、FIFO、消息队列、共享内存等核心概念。
文件IO编程:掌握文件描述符、文件读写接口、原子操作、阻塞与非阻塞IO等技术。
多任务和多进程编程:学习进程表示、用户表述、fork与vfork、异步与多线程概念、线程同步等技术。
网络编程:掌握网络基本概念、套接口编程、网络字节序、Client、Server 结构、UDP 编程等。
数据库:SQLite3 是一个非常受欢迎和常用的嵌入式数据库解决方案,它因其轻量级和高效性而被广泛采用。
GNU 创始人 Richard Stallman(RMS)过去一段时间一直在编写一份 GNU C 语言的学习手册,2022年9月他发布公告,正式公开了这一成果 —— GNU C Language Intro and Reference Manual(GNU C 语言介绍和参考手册),其中涵盖了 GNU 对 C 编程语言的扩展。本手册解释了在 GNU/Linux 系统和其他系统上与 GNU Compiler Collection (GCC) 一起使用的 C 语言,通常其称之为 GNU C。如果已经了解了 C 语言,则可以将它用作参考手册;如果只了解基本的编程概念,但对 C 语言一无所知,那么可以从头开始按顺序阅读本手册以学习 C 语言。
参考:
业界对C语言的相关评测
C语言之父和Linux之父谁更伟大
阮一峰发布的《C语言入门教程》
List of C-family programming languages
Richard Stallman发布的《GNU C 语言介绍和参考手册》