目录
1.C语言是什么?
人和人交流,使用的是自然语言,比如:汉语、英语、日语
那么人和计算机怎么交流?使用计算机语言
人们可以使用计算机语言编写程序,给计算机下达指令
C语言就是众多计算机语言中的一种,常见的计算机语言还有:C++、Java、Go、Python
2.C语言的历史
在介绍C语言的历史之前,我们不妨先了解一下机器语言→汇编语言→高级语言这3个阶段。
2.1 机器语言
人们最早使用的计算机语言,并不是C语言,而是机器语言。
机器语言是计算机唯一能直接识别和执行的二进制代码,是最底层的语言。
但是机器语言有个缺点,就是特别复杂,比如1+1用机器语言表示可能是如下这样:
00000001 00000001 00000010 00000001 11111111
这样实在是太冗杂,太难以识记了,于是汇编语言应运而生。
2.2 汇编语言
汇编语言使用了一些助记符如英文单词,来代替机器语言的指令,比如:用ADD表示加法,SUB表示减法。
用汇编语言写的程序,必须通过汇编器转换为机器语言,才能被计算机执行。
汇编语言看起来已经很友好了,但是还是不够好,于是高级语言应运而生。
2.3 高级语言
高级语言更接近人类的自然语言,更容易理解。
高级语言分为编译型语言和解释型语言。
比如:C语言属于编译型语言,Python属于解释型语言。
编译型语言和解释型语言都需要转换成机器语言才能被计算机执行,只是转换的方式和时机有所不同。
在转换时机方面:
编译型语言是在执行前,一次性编译完,然后再执行,类似于提前把整本书翻译成目标语言,阅读时直接读译本,高效但不灵活。
解释型语言是在执行时,逐行解释,解释一行再执行一行,类似于边读边翻译,阅读时需要翻译官实时翻译,灵活但低效。
在转换方式方面:
编译型语言需要先通过编译器转换为汇编语言,再通过汇编器转换为机器语言,最终才能被执行。
解释型语言则没有汇编这个过程,直接通过解释器或虚拟机执行,可能生成抽象的字节码。
2.4 C语言的历史
C语言最初是作为Unix系统的开发工具而发明的。
发明B语言:1969年,贝尔实验室的肯·汤普森和丹尼斯·里奇一起开发了Unix操作系统,Unix是用汇编语言写的,汇编语言不具备可移植性,简单来说,汇编语言是为特定硬件定制的代码,一旦硬件换了,代码就得重写。为了移植到其他计算机,汤普森就在BCPL语言的基础上发明了B语言。
发明C语言:1972年,丹尼斯·里奇和布莱恩·柯林汉又在B语言的基础上重新设计了一种新语言,这种新语言取代了B语言,所以称为C语言。
Unix系统使用C重写:1972年,整个Unix系统都使用C语言重写,此后,C语言开始快速流传,广泛用于各种操作系统和系统软件的开发。
C语言标准化:1988年,美国国家标准协会(ANSI)正式将C语言标准化,明确语法规则、功能特性及实现要求等,标志着C语言开始稳定和规范化。
至今:一直到今天,C语言还是在广泛使用,在计算机语言的排行榜上霸占前三。
3.用什么软件来写C语言程序?
在初步了解C语言之后,你可能会蹦出一个问题:那么我们用什么软件来写C语言程序呢?
在回答这个问题之前,我们不妨先了解一些必要的概念。
3.1 编译和链接
C语言源代码本质上就是一堆文本,计算机是读不懂这些文本的,必须通过编译器的编译,转换成计算机能够识别的二进制代码,再经过链接器的链接,生成二进制的可执行文件,最终才能够被执行。
一个工程一般都会由多个源文件组成,下图演示了源程序经过编译器和链接器处理的过程:
- 源文件以 .c 为后缀
- 目标文件以 .obj 为后缀
- 可执行文件以 .exe 为后缀
- 每个源文件单独经过编译器的处理生成对应的目标文件
- 多个目标文件和库文件经过链接器的处理生成对应的可执行程序
3.2 常见的编译器
上面我们已经提到过,C语言是一门编译型语言,需要依赖编译器,将计算机语言转换成计算机能够执行的机器指令。
常见的C语言编译器有:msvc、clang、gcc等
-
msvc:是 Microsoft Visual C++ 的缩写,是微软公司开发的。
-
clang:主要用于苹果生态开发。
-
gcc:是一款历史悠久的开源编译器套件,尤其适合 Linux/Unix 环境下的系统开发、嵌入式项目以及学术研究。
3.2 集成开发环境
我们再来看一下集成开发环境的概念吧,说白了,所谓集成开发环境,就是你写代码要用的软件。
集成开发环境(IDE):是用于提供开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。是集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。
常见的集成开发环境有:VS2022、XCode、CodeBlocks、DevC++、Clion等。
- VS2022:集成了msvc。安装包较大一些,安装简单,无需多余配置,使用起来非常方便。
- Xcode:集成了clang。是苹果公司推出的集成开发环境。
- CodeBlock:集成了gcc。这个工具比较小众,需要配置环境,不太推荐。
- DevC++:集成了gcc。小巧,但是工具过于简单,对于代码风格的养成不好,一些竞赛使用。
- Clion:默认使用CMake,编译器是可以配置的。工具是收费的,所以暂时不推荐使用。
最后,我们来回答一下,用什么软件来写C语言程序?
我推荐安装VS2022的社区版本进行学习,免费,界面清爽,使用方便,工作中常见。
4.写一个简单的C语言程序
来吧,我们直接上代码:
运行结果如下:
上图演示了使用VS2022编写的一个简单经典程序,并附带运行结果。
顺便说一下,在VS2022中,运行代码的快捷键是:Ctrl + F5 或 Ctrl + Fn +F5(因为有的电脑,F5的默认功能可能是调低亮度,需要联用Fn才能恢复它原本的功能)
接下来,我们就以上面的代码为例,延伸到其他的概念吧。
5.main函数
每个C语言程序,不管有多少行代码,都是从main函数(也叫主函数)开始执行的,也就是说,main函数是程序的入口。
main 前面的 int 表示,main函数执行结束的时候,会返回一个整型类型的值,所以在main函数的最后写 return 0; 正好前后呼应。
每个程序都必须有一个main函数,因为程序是从main函数开始执行的,如果没有main函数,程序就无从执行了。
每个程序只能有一个main函数,因为如果有多个main函数,程序就不知道到底该从哪个入口开始执行了。
6.printf函数与库函数
6.1 printf函数
上面的代码中,有一行 printf("hello world\n"); 使用了printf函数。
printf函数是一个库函数,功能是在屏幕上打印出想要的信息,只要把想打印的一串字符放在双引号中,就可以实现打印功能。
同时,在使用库函数的时候,是需要包含头文件的,比如:printf函数需要包含的就是 stdio.h 这个头文件。具体写法就是:#inclulde <stdio.h>
而 hello world 后面的 \n ,是换行符,作用是在打印完这行内容之后,进行一次换行。
下面的两张图,对比了使用换行符和不使用换行符的情况,可以一目了然地看出换行符\n的作用。
printf("hello world\n); 所打印的信息,是字符串类型,用双引号括起来。
其实printf函数还可以打印其他类型的信息,如下图所示:
上面的 %c 、 %d 等,是占位符,会被后边的值替换。
6.2 库函数
我们刚刚说,printf函数是一个库函数。
那么,也许你会问,什么是库函数呢?
我们以printf函数为例,看看它的部分简化版底层实现代码:
我们不用关心上面的代码是什么意思,我们只需要知道一点,要实现打印功能,原来要写这么多行代码!
如果我们在一个程序中,需要多次使用到打印功能,那么岂不是要重复地写很多遍这样的长代码吗?这实在是太麻烦和低效了。于是C语言标准规定了一个函数,命名为printf函数。这样,以后人们想要实现打印功能,只需要一行简单的 printf("......"); 就可以搞定了。
推而广之,如果我们在一个程序中,需要多次使用到某种功能,可能要重复写同一段很长的代码。所以C语言标准规定了一组函数,这些函数再由不同的编译器厂商根据标准进行实现,提供给大家使用。这些函数组成了一个函数库,被称为标准库,里面的函数被称为库函数。在这个基础上,一些编译器厂商可能会额外扩展,提供部分其他函数,对于这些函数,其他的编译器不一定支持。
属于同一个系列的库函数,一般会声明在同一个头文件中,所以库函数的使用需要包含对应的头文件。
7.关键字
在写代码的过程中,我们常常需要创建变量或者创建函数等,既然要创建变量,那么肯定要取一个变量名吧?创建函数,也需要取一个函数名吧?
那么,这些名字是随便我们取的吗?其实,有些名字我们是不能取的,这些不能取的名字,就称为关键字或保留字,比如前面提到的 int 和 return 等。
总结一下:我们自己在创建标识符的时候,是不能和关键字重复的。
那么你可能要问了,关键字都有哪些呀?
C语言的32个关键字如下:
注:在C99标准中,加入了 inline 、 restrict 、 _Bool 、 _Complex 、 _Imaginary 等关键字。
8.字符和ASCII编码
在键盘上,可以敲出各种字符,比如: h 、 j 、 @ 、 # 等,这些符号都被称为字符。C语言中,字符是用单引号括起来的,如:'h','j','@'。
我们知道,在计算机中,所有的数据都是以二进制的形式存储的,可是字符该怎么存储呢?
不难想到,我们可以给每个字符编一个二进制序列,这就叫做编码。每个字符都唯一对应一个二进制序列,这样就可以解决存储的问题了。
为了统一编码规则,不造成混乱,后来美国国家标准学会(ANSI)出台了一个标准ASCII编码,C语言中的字符就遵循了ASCII编码的方式。
上面的ASCII码表,我们看每个字符对应的十进制数字就好了。
我们不需要记住所有的内容,使用时查看就可以了,但我们也可以记住几组特殊的数据:
- 字符A~Z的ASCII码值,从65~90
- 字符a~z的ASCII码值,从97~122
- 对应的大小写字符,ASCII码值的差值为32,即小写字符 - 大写字符 = 32
- 数字字符0~9的ASCII码值,从48~57
- 换行符 \n 的ASCII码值,为10
- 在这些字符中,ASCII码值从0~31的32个字符是不可打印的,无法打印在屏幕上观察
前面我们提到过,单个字符的打印可以用%c来指定格式,而且可以用ASCII码值来打印对应的字符:
另外,有些字符是不可打印的,这里可以展示一下可打印的字符:
9.字符串和\0
在前面,已经提到过字符串了,使用双引号括起来的一串字符就被称为字符串,如:"hello world" 就是一个字符串。
前面也提到过,字符串的打印格式可以用%s来指定,也可以直接打印,如下:
C语言字符串中,有一个特殊的知识,就是在字符串的末尾,放着一个看不见的 \0 字符,这个\0字符是字符串的结束标志。
如上图,对于字符串"abcdef",我们实际上只看到了6个字符:a,b,c,d,e,f
但是实际上,在末尾还隐藏了一个转义字符:\0
刚刚说到,\0是字符串结束的标志,所以,在使用printf()打印字符串或者strlen()计算字符串长度的时候,遇到\0就自动停止了。
在C语言中,也可以把一个字符串放在一个字符数组中,我们可以利用下面的代码来验证一下\0的功能:
我们再来看一下运行结果:
我们可以看到,arr1数组在打印的时候,打印了abc,后面还有一些随机值,这就是因为arr1在末尾,没有\0字符作为结束标志,于是在打印的时候没有停止。
而arr2的打印就是完全正常的,因为arr2数组中放入的是字符串常量,默认自带\0字符,打印可以正常停止。
如果我们在arr1数组的末尾再放一个 '\0' ,会如何呢?
如上图,三次打印的结果都一样了。
我们在arr1数组的末尾加了一个'\0',所以打印可以正常停止,不会往后出现随机值。
我们在arr3数组中,abc的后面加了一个'\0',所以在此处打印就停止了,def并不会被打印。
10.转义字符
在前面,我们提到了 \n 和 \0 等字符,它们都是转义字符。
所谓转义字符,顾名思义:就是转变原来意思的字符。
比如:n本来是一个字母,但是加上斜杠之后,就转变成了换行的意思。
还是用代码演示一下吧:
由上图可见,双引号中所放的内容是abc\ndef,但\n并没有被打印出来,而是被转换为换行。
像这样的转义字符,C语言中还有很多,我们不妨来看看:
- \' :用于表示单引号。
- \" :用于表示字符串内部的双引号。
- \\ :用于表示一个反斜杠,防止它被解释为一个转义序列符。
- \a :警报,发出警报声或出现闪烁,或者两者同时发生。
- \b :退格键,光标回退一个字符,但不删除字符,如果后续有新的字符输出,新字符就会把原位置的字符覆盖掉。比如打印abc\bd,打印结果是abd。这个过程是,首先输出字符'a','b','c',此时光标位于'c'的后面。遇到\b之后,光标回退到'b'和'c'之间,接着输出'd','d'便覆盖了原来的'c'。也就是说,没有删掉原来的字符,只是把原来的字符覆盖掉了。
- \n :换行符,前面已演示过。
- \r :回车符,光标移到当前行的起始位置,也就是最左侧,但不删除原有内容,后续要是有新字符输出,新字符就会把原位置的字符覆盖掉。比如打印abc\rdef,打印结果是def。这个过程是,首先输出abc,遇到\r,光标移到这一行的开头,也就是a的前面,然后再输出def,而def把原来位置上的abc覆盖了。
- \t :水平制表符,光标移到下一个水平制表位,通常是8。比如打印AAA\tAAA,打印结果是AAA(5个空格)AAA。这个过程是,先输出AAA,然后遇到\t,发现AAA后面离8还差5个位置,于是补充5个空格,光标跳转到下一个水平制表位的最左侧。
- \v :垂直制表符,光标移到下一个垂直制表位,也就是移到下一行的相同列位置。
- \ddd :字符的8进制表示形式。其中d为1个8进制数字,可以是\d,\dd,\ddd。比如:\110,八进制的110表示的是字符H"。
- \xdd :字符的16进制表示形式。其中dd为2个16进制数字。比如:\x4a表示字符'J',\x36表示字符'0'。
- \0 :null字符,表示没有内容。\0其实就是\ddd这类转义字符的一种, 用于字符串的结束标志,其ASCII码值是0。
下面来个关于有关转义字符的小题目吧:
思考一下,运行结果会是什么呢?答案如下:
11.语句分类
显然, C语言的代码,是由一条条的语句构成的,这些语句可以分为五类:
- 空语句
- 表达式语句
- 函数调用语句
- 复合语句
- 控制语句
11.1 空语句
空语句是一个仅由分号组成的语句,它不执行任何操作。
空语句看似简单,但在特定场景下有重要用途。
空语句出现的场景一般是:在语法上,这里需要一条语句,但这个语句不需要做任何事。
下面给出两种简单的应用场景:
11.2 表达式语句
表达式语句就是,一个表达式后面跟一个分号。
那么啥是表达式呢?其实表达式也分为挺多种的,比如:赋值语句、函数调用语句、自增/自减语句、逗号表达式语句、空表达式语句等等。
11.3 函数调用语句
函数调用语句就是调用函数的语句,后面也需要跟上一个分号。
调用函数,可以是调用库函数,也可以是调用自定义函数。
下面是调用库函数的示例:
下面是调用自定义函数的示例:
11.4 复合语句
复合语句也就代码块,简而言之,就是由被花括号{}括起来的一块代码。
在一对花括号内部,会形成独立的作用域。在花括号内部声明的变量,具有局部作用域,在该花括号之外,无法访问这个变量。
11.5 控制语句
控制语句用于控制程序的执行流程,以实现各种结构(C语言支持3种结构:顺序结构、选择结构、循环结构)。
控制语句可分为以下3类,共9种:
- 条件判断语句(也叫分支语句):if语句、switch语句。
- 循环执行语句:do while语句、while语句、for语句。
- 转向语句:break语句、goto语句、continue语句、return语句。
这些语句,后续再进行一一介绍。
12.注释
注释是对代码的说明,编译器会自动忽略注释,所以注释对代码本身不会产生影响。
写了注释,可以帮助我们更好地理解代码,当然也不能过度写注释,写不必要的注释。
12.1 注释的2种形式
12.1.1 /**/的形式
第一种方法是,把注释内容放在 /*...*/ 之间,内部可以分行。
在使用这种注释的时候,要注意不要忘记写结束的符号 */ ,我们来看一个反面例子:
我们来看一下运行结果吧:
12.1.2 //的形式
第二种写法,是把注释内容放在双斜杠//的后面,从双斜杠开始,一直到这一行的行尾,都属于注释内容。
这种注释方法只能是单行的,可以放在行首,也可以放在一句代码的结尾。
最后,不管是/*/的注释方法,还是//的注释方法,都不能放在双引号里面,否则会成为字符串的一部分,解释为普通符号,失去注释的作用:
12.2 注释会被替换
编译时,注释会被替换成一个空格。
比如:Hello/*这里是注释*/World 会被替换成Hello World,而不是HelloWorld。
好啦,这一篇博客完结了。