目录
- (一)从Hello,World开始
- (二)第一个C语言程序, C语言的整体框架
- (三)输入法的选择(中/英),全角半角的区别
- (四)什么是源文件?
- (五)C语言的编译和链接
- (六)C语言编译器(主流)
- (七)什么是IDE(集成开发环境)?
- (八)什么是工程/项目?
- (九)哪款C语言编译器(IDE)适合初学者?
- (十)C语言的三套标准:C89、C99和C11
- (十一)C语言为什么会有那么多编译器?
- (十二)程序在安装过程中都做了什么?
- (十三)C语言程序的错误和警告
- (十四)分析第一个C语言程序
(一)从Hello,World开始
我们一般有两种方式从计算机上获取信息,一是看屏幕上的文字、图片、视频等,另一种是听喇叭发出的声音。让喇叭发出声音目前阶段还比较麻烦,我们先看如何在屏幕上显示一些文字吧。
在屏幕上显示逾矩非常简单,只需要一个语句,例如,下面的代码会让屏幕上显示“Hello,World”。
puts(“Hello,World”);
这里有一个生疏词汇“puts”,用来让计算机在屏幕上显示文字。
更加专业的称呼:
- 在屏幕上显示文字叫输出(Output)
- 每个文字都是一个字符(Character)
- 多个字符组合起来,就是一个字符序列,叫字符串(String)
“puts”是output string的缩写,意思是“输出字符串”。
在C语言中,字符串一般需要用""包围起来,Hello,World什么也不是,计算机并不认识他,"Hello,World"才是字符串。
puts在输出字符串的时候,需要将字符串放在()内
在汉语和英语中,一般用“。”和“.”表示一句话的结束,而在C语言中,使用“;”表示一个语句的结束。
puts(“Hello,World”)表达了完整的意思,是一个完整的语句,需要在最后加上“;”,表示当前语句结束了。
总结起来,上面的语句可以分为三部分:
- “puts()”命令计算机输出字符串
- “Hello,World”是要输出的内容
- “;”表示语句结束
(二)第一个C语言程序, C语言的整体框架
puts可以在显示器上输出内容,但是仅有puts是不够的,程序不能运行,还要添加其他代码,构成一个完整框架,完整框架如下:
#include <stdio.h>
int main()
{
puts("Hello, World!\n");
return 0;
}
第1~3行、第5~6行是固定的,所有C语言都必须有这几行,暂时不需要了解是什么意思,之后我们慢慢讲解。
先记住,今后我们写的所有类似puts这样的语句,都必须放在{ }之间才有效。
另外,颜色是为了区分不同的语义,这个我们后面会逐渐了解到。
(三)输入法的选择(中/英),全角半角的区别
计算机起源于美国,C语言、C++、Java、JavaScript等很多流行的编程语言都是美国人发明的,所以在编写代码的时候必须使用英文半角输入法,尤其是标点符号。一定要注意。
例如,上文我们使用puts语句在显示器上显示的内容:
puts(“Hello,World”);
这里的括号、双引号、分号都必须是英文符号,而且是半角的。
全角和半角的区别在于除了汉字以外的其他字符,比如标点符号、英文字母、阿拉伯数字等,全角和半角字符所占用的位置大小不同。在计算机屏幕上,一个汉字要占两个英文字符的位置,人们把一个英文字符所占的位置称为“半角”,相对的把一个汉字所占的位置称为“全角”。
标点符号、英文字母、阿拉伯数字这些字符不同于汉字,在半角状态下他们被当作英文字符处理,在全角状态下被作为中文字符处理,请看下面的例子:
半角输入:
Hello C,I like!
全角输入:
Hello C,I like!
另外,很重要的是,相同的字符在半角和全角的状态下对应的编码值(如Unicode编码,GBK编码等)不一样,所以他们是不同字符
(四)什么是源文件?
在软件开发的过程中,我们需要将我们编写的代码(Code)保存到一个文件中,这样代码才不会丢失,才能够被编译器(后文解释)找到,才能最终变成可执行文件。这种用来保存代码的文件就叫源文件(Source File)
源文件其实就是纯文本文件,他的内部并没有特殊的格式。但是每种编程语言的源文件都有特定的后缀,以方便被编译器识别,被程序员理解。源文件的后缀大多根据编程语言本身的名字来命名,比如:
- C语言源文件的后缀是.c
- C++语言(C Plus Plus)源文件后缀是.cpp
- Java源文件后缀是.java
- Pthyon源文件后缀是.py
- JavaScript源文件后缀是.js
另外,C++是在C语言的基础上发展起来的,是C语言的扩展,C++也包含了C语言的全部内容,将C语言放在.cpp文件中不会有错,但是我还是强烈建议把C语言代码放在.c文件中,这样能更加严谨的遵循C语言的语法,也能够更加清晰的了解C语言和C++的区别。
(五)C语言的编译和链接
我们平时所说的程序就是指双击后能够直接运行的程序,这样的程序称为可执行程序(Executable Program)。在WIndows下,可执行程序的后缀有.exe或.com(其中.exe比较常见);在类UNIX系统(Linux,Mac OS等)下,可执行程序没有特定后缀,系统根据头文件信息来判断是否是可执行程序。
可执行程序的内部是一系列计算机指令和数据的集合,他们都是二进制形式的,CPU可以直接识别,毫无障碍。但是对于程序员,他们非常晦涩,难以记忆和使用。
例如在屏幕上输出“Hello world”,C语言的写法为
puts(“Hello world”);
二进制的写法为
感受一下这个复杂的写法。
在计算机发展初期,程序员就是这样使用二进制的命令来编写程序的,那个年代还没有编程语言。
直接使用二进制指令编程对程序员来说简直是噩梦,尤其当程序比较大的时候,不断编写麻烦,还要频繁的查询手册,出错也异常难处理,因为要面对一堆二进制数据。另外,二进制指令编程步骤繁琐,要考虑各种边界情况和底层问题,开发效率十分低下。
这就倒逼程序员开发出了编程语言,提高自己的生产力。例如汇编、C语言、C++、Java、Python、Go语言等。
编译(Compile)
C语言的代码由固定的词汇按照固定的格式组织起来,简单直观,程序员容易理解,但是对于CPU,C语言代码简直就是天书,根本不认识,CPU只认识几百个二进制指令。这就需要一个工具,将C语言的代码转换成CPU能够识别的二进制指令,也就是将代码加工成.exe程序的格式。这个工具是一个特殊的软件,叫做编译器(Compiler)。
编译器能够识别代码中的词汇、句子以及特定的格式,并将他们转换成计算机能够识别的二进制形式,这个过程称编译(Compile)。
编译也可以理解为翻译,类似于将中文翻译成英文,他是一个复杂的过程,大致包括词法分析、语法分析、语义分析、性能优化、生成可执行文件五个步骤,期间涉及到复杂的算法和硬件结构,有兴趣的自行阅读《编译原理》。
不了解编译原理不影响我们学习C语言,我也不建议初学者去钻研编译原理,不要把自己绕进去。
C语言的编译器有很多种,不同平台下有不同的编译器:
- WIndows下常用的是微软开发的Visual C++,他被集成在Visual Studio中,一般不单独使用
- Linux下常用的是GUN组织开发的GCC
- Mac下常用的是LLVM/Clang,他被集成在Xcode中(Xcode以前集成的是GCC,后来由于GCC不配合才改为LLVM/Clang,LLVM/Clang的性能比GCC更强大)
你的代码正确与否,编译器说了才算,我们学习C语言,从某种意义上是学习如何使用编译器。
编译器可以100%保证你的代码从语法上是正确的,哪怕有一点小小的错误,编译也不能通过,编译器也会告诉你哪里错了,方便你修改。
链接(Link)
C语言代码经过编译后,并没有生成最终的可执行文件,而是生成了一种叫做**目标文件(Object File)**的中间文件(或者说临时文件),目标文件也是二进制形式的,他和可执行文件是一致的。对于Visual C++,目标文件的后缀是.obj;对于GCC,目标文件的后缀是.o。
目标文件经过链接(Link)后才能变成可执行文件。既然目标文件和可执行文件的格式是一样的,为什么还要再链接一次呢,直接作为可执行文件不行吗?
不行的!因为编译只是把我们自己写的代码变成了二进制形式,他还需要和系统组件(比如标准库、动态链接库)结合起来,这些组件都是程序运行所必须的。
链接(Link)其实就是一个“打包”的过程,他将所有二进制形式的目标文件和系统组件组合成一个可执行文件。完成链接的过程也需要一个特殊的软件,叫链接器(Linker)。
随着我们代码越写越多,最终需要将他们分散在多个源文件中。编译器每次只能编译一个源文件,生成一个目标文件,这个时候,链接器除了将目标文件和系统组件结合起来,还需要将编译器生成的多个目标文件组合起来。
再次强调,编译是针对一个源文件的,有多少个源文件就会编译多少次,就会生成多少个目标文件。
总结
不管我们编写的代码有多简单,都必须经过【编译–>链接】的过程才能生成可执行文件
- 编译就是将我们编写的源代码“翻译”成计算机可以识别的二进制形式,他们以目标文件的形式存在
- 链接就是一个“打包”的过程,它将所有的目标文件以及系统组件组合成一个可执行文件
另外,如果不是特别强调,一般情况下我们所说的编译器也包含了链接器。
(六)C语言编译器(主流)
由于C语言历史悠久,早起规范也相对较少,计算机产业也处于拓荒的时代,所以涌现了很多C语言编译器,他们各有特点,适用于不同平台,下面我们将介绍一下
桌面操作系统
对于当前主流桌面操作系统而言,可使用 Visual C++、GCC 以及 LLVM Clang 这三大编译器。
Visual C++(简称 MSVC)是由微软开发的,只能用于 Windows 操作系统;GCC 和 LLVM Clang 除了可用于 Windows 操作系统之外,主要用于 Unix/Linux 操作系统。
像现在很多版本的 Linux 都默认使用 GCC 作为C语言编译器,而像 FreeBSD、macOS 等系统默认使用 LLVM Clang 编译器。由于当前 LLVM 项目主要在 Apple 的主推下发展的,所以在 macOS中,Clang 编译器又被称为 Apple LLVM 编译器。
MSVC 编译器主要用于 Windows 操作系统平台下的应用程序开发,它不开源。用户可以使用 Visual Studio Community 版本来免费使用它,但是如果要把通过 Visual Studio Community 工具生成出来的应用进行商用,那么就得好好阅读一下微软的许可证和说明书了。
而使用 GCC 与 Clang 编译器构建出来的应用一般没有任何限制,程序员可以将应用程序随意发布和进行商用。
MSVC 编译器对 C99 标准的支持就十分有限,加之它压根不支持任何 C11 标准,所以本教程中设计 C11 的代码例子不会针对 MSVC 进行描述。所幸的是,Visual Studio Community 2017 加入了对 Clang 编译器的支持,官方称之为——Clang with Microsoft CodeGen,当前版本基于的是 Clang 3.8。
也就是说,应用于 Visual Studio 集成开发环境中的 Clang 编译器前端可支持 Clang 编译器的所有语法特性,而后端生成的代码则与 MSVC 效果一样,包括像 long 整数类型在 64 位编译模式下长度仍然为 4 个字节,所以各位使用的时候也需要注意。
为了方便描述,本教程后面涉及 Visual Studio 集成开发环境下的 Clang 编译器简称为 VS-Clang 编译器。
嵌入式系统(作为了解)
而在嵌入式系统方面,可用的C语言编译器就非常丰富了,比如:
- 用于 Keil 公司 51 系列单片机的 Keil C51 编译器;
- 当前大红大紫的 Arduino 板搭载的开发套件,可用针对 AVR 微控制器的 AVR GCC 编译器;
- ARM 自己出的 ADS(ARM Development Suite)、RVDS(RealView Development Suite)和当前最新的 DS-5 Studio;
- DSP 设计商 TI(Texas Instruments)的 CCS(Code Composer Studio);
- DSP 设计商 ADI(Analog Devices,Inc.)的 Visual DSP++ 编译器,等等。
通常,用于嵌入式系统开发的编译工具链都没有免费版本,而且一般需要通过国内代理进行购买。所以,这对于个人开发者或者嵌入式系统爱好者而言是一道不低的门槛。
不过 Arduino 的开发套件是可免费下载使用的,并且用它做开发板连接调试也十分简单。Arduino 所采用的C编译器是基于 GCC 的。
还有像树莓派(Raspberry Pi)这种迷你电脑可以直接使用 GCC 和 Clang 编译器。此外,还有像 nVidia 公司推出的 Jetson TK 系列开发板也可直接使用 GCC 和 Clang 编译器。树莓派与 Jetson TK 都默认安装了 Linux 操作系统。
在嵌入式领域,一般比较低端的单片机,比如 8 位的 MCU 所对应的C编译器可能只支持 C90 标准,有些甚至连 C90 标准的很多特性都不支持。因为它们一方面内存小,ROM 的容量也小;另一方面,本身处理器机能就十分有限,有些甚至无法支持函数指针,因为处理器本身不包含通过寄存器做间接过程调用的指令。
而像 32 位处理器或 DSP,一般都至少能支持 C99 标准,它们本身的性能也十分强大。而像 ARM 出的 RVDS 编译器甚至可用 GNU 语法扩展。
下图展示了上述C语言编译器的分类。
(七)什么是IDE(集成开发环境)?
实际开发中,除了编译器是必备的工具,我们往往还需要很多其他辅助软件,例如:
- 编辑器:用来编写代码,并给代码着色,方便阅读
- 代码提示器:输入部分代码,即可提示全部代码,加速代码编写
- 调试器:观察程序的每个运行步骤,帮我我们发现程序的错误
- 项目管理工具:对程序涉及的所有资源进行管理,包括源文件、图片、视频、第三方库等
- 漂亮的界面:各种按钮、菜单、窗口等控件
这些工具通常被打包到一起,统一发布和安装,例如Visual Studio、Dev C++、Xcode、Jetbrains Clion等,他们统称为集成开发环境(IDE,Integrated Development Environment)
IDE就是一系列开发工具的组合套装。这就好比台式机,一个台式机的核心部件是主机,有了主机就能独立工作了,但是在我们购买台式机时,往往还要附带上显示器、键盘、鼠标等外围设备,因为只有主机太不方便了,必须有外设才能玩的爽。
(八)什么是工程/项目?
在实际开发中,程序员会将代码分门别类的放在多个源文件中。除了这些代码源文件,一个程序往往还需要包含图片、视频、音频、控件、库等其他资源,他们也都是一个个的文件。
为了有效管理这些文件,我们有理由把他们都放到同一个目录(文件夹)下,并且这个目录下只存放当前程序有关的资源。实际上,IDE也是这么做的,他会为每个程序都创建一个专门的目录,将所有用到的文件都集中到这个目录下,并对他们进行便捷的管理,比如重命名、删除、编辑等。
这个为当前程序专用的文件夹,在IDE中有个专门的称呼,叫做“Project”,翻译过来就是工程或者项目。在Visual C++6.0下,这叫做一个“工程”,而在Visual Studio下,这又叫做一个“项目”。只是单词“Project”翻译不同而已,其实是一个道理。
工程类型/项目类型
程序是一个比较宽泛的称呼,他可以细分为很多类,比如:
- 有的程序不带界面,完全是“黑屏”的,只能输入一些字符或者命令,称为**“控制台程序(Console Application)”**,例如Windows下的cmd.exe,Linux或Mac OS下的终端(Terminal)
- 有的程序带界面,看起来很漂亮,可以用鼠标点击交互,称为**“GUI程序(Graphical User Interface Program)”**,例如QQ、迅雷、Chrome等
- 有的程序不单独出现,而是作为其他程序的一个组成部分,比如静态库、动态库等
不同的程序对应不同的工程类型(项目类型),使用 IDE 时必须选择正确的工程类型才能创建出我们想要的程序。换句话说,IDE 包含了多种工程类型,不同的工程类型会创建出不同的程序。
不同的工程类型本质上是IDE中各个参数的不同设置。
(九)哪款C语言编译器(IDE)适合初学者?
C语言有如此多的编译器,尤其是Windows下,多如牛毛,本节我们将针对Windows、Linux、Mac OS三大平台进行讲解。
Windows下推荐的IDE
Windows 下首先推荐大家使用微软开发的 Visual Studio(简称 VS),它是 Windows 下的标准 IDE,实际开发中大家也都在使用。
为了适应最新的 Windows 操作系统,微软每隔一段时间(一般是一两年)就会对 VS 进行升级。VS 的不同版本以发布年份命名,例如 VS2010 是微软于 2010 年发布的,VS2017 是微软于 2017 年发布的。
对于初学者,我推荐使用 VS2015。最好不用使用 VS2017,有点坑初学者。
现在VS稳定版已经更新到2019了,也比较推荐使用。
大家去官网自行下载即可
Linux下推荐的IDE
Linux 下可以不使用 IDE,只使用 GCC 编译器和一个文本编辑器(例如 Gedit)即可,这样对初学者理解C语言程序的生成过程非常有帮助
当然,如果你希望使用 IDE,那么可以选择 CodeLite、Code::Blocks、Anjuta、Eclipse、NetBeans 等。
Mac OS下推荐的IDE
Mac OS 下推荐使用 Apple 官方开发的 Xcode,在 APP Store 即可下载
另外,Visual Studio 也推出了 Mac 版本,已经习惯了 Visual Studio 的用户可以高兴一把了。
另外推荐
另外,jetbrains家的软件也很不错,针对目前几乎所有编程语言,都有着比较好的支持。
(十)C语言的三套标准:C89、C99和C11
今天我们所使用的Windows、Linux、Mac OS等操作系统都是由一种叫做Unix的系统演化而来。Unix作为80年代的主流操作系统,是整个软件工业的基础,是现代操作系统的鼻祖,C语言就是为Unix而生的。
Unix和C语言的开发者都是同一人,名字叫丹尼斯·里奇(Dennis MacAlistair Ritchie)
2011年10月12日(北京时间为10月13日),丹尼斯·里奇去世,享年70岁。
C语言的诞生
1967年,26岁的丹尼斯·里奇进入贝尔实验室开发 Unix,并于 1969 年圣诞节前推出第一个试运行版本。这个时候的 Unix 是用汇编语言写的,移植性非常差,要想让 Unix 运行在不同型号的机器上,就得针对每个型号的机器重写一遍操作系统,这显然是一个不可能完成的任务。
为了提高通用性和开发效率,丹尼斯·里奇决定发明一种新的编程语言——C语言。紧接着,丹尼斯·里奇就用C语言改写了 Unix 上的C语言编译器,他的同事汤姆森则使用C语言重写了 Unix,使它成为一种通用性强、移植简单的操作系统,从此开创了计算机编程史上的新篇章,C语言也成为了操作系统专用语言。
C89(C90)标准
到了80年代,C语言越来越流行,广泛被业界使用,从大型主机到小型微机,各个厂商群雄并起,推出了多款C语言的编译器。这些编译器根据行业和厂商自己的需求,进行了各种扩展,C语言进入了春秋战国时代,逐渐演变成一个松散杂乱的大家族。
为统一C语言版本,1983 年美国国家标准局(American National Standards Institute,简称 ANSI)成立了一个委员会,专门来制定C语言标准。1989 年C语言标准被批准,被称为 ANSI X3.159-1989 “Programming Language C”。这个版本的C语言标准通常被称为 ANSI C。又由于这个版本是 89 年完成制定的,因此也被称为 C89。
后来 ANSI 把这个标准提交到 ISO(国际化标准组织),1990 年被 ISO 采纳为国际标准,称为 ISO C。又因为这个版本是1990年发布的,因此也被称为 C90。
ANSI C(C89)与 ISO C(C90)内容基本相同,主要是格式组织不一样。
因为 ANSI 与 ISO 的C标准内容基本相同,所以对于C标准,可以称为 ANSI C,也可以说是 ISO C,或者 ANSI / ISO C。以后大家看到 ANSI C、ISO C、C89、C90,要知道这些标准的内容都是一样的。
目前常用的编译器,例如微软编译器、GCC、LLVM/Clang 等,都能很好地支持 ANSI C 的内容。
C99标准
在 ANSI C 标准确立之后,C语言的规范在很长一段时间内都没有大的变动。1995 年C程序设计语言工作组对C语言进行了一些修改,增加了新的关键字,编写了新的库,取消了原有的限制,并于 1999 年形成新的标准——ISO/IEC 9899:1999 标准,通常被成为 C99。
但是这个时候的C语言编译器基本已经成熟,各个组织对 C99 的支持所表现出来的兴趣不同。当 GCC 和其它一些商业编译器支持 C99 的大部分特性的時候,微软和 Borland 却似乎对此不感兴趣,或者说没有足够的资源和动力来改进编译器,最终导致不同的编译器在部分语法上存在差异。
例如,ANSI C 规定,局部变量要在函数开头定义,而 C99 取消了这个限制,变量可以在任意位置定义,后续说到变量时再详细介绍。
C11标准
C11 标准由国际标准化组织(ISO)和国际电工委员会(IEC) 旗下的C语言标准委员会于 2011 年底正式发布,支持此标准的主流C语言编译器有 GCC、LLVM/Clang、Intel C++ Compile 等。
C11 标准主要增加了以下内容:
- 增加了安全函数,例如 gets_s()、fopen_s() 等;
- 增加了 <threads.h> 头文件以支持多线程;
- 增加了 <uchar.h> 头文件以支持 Unicode 字符集;
- 以及其它一些细节。
总结
现有的教程(包括书籍、视频、大学课程等)大都是针对 C89 编写的,这是C语言的核心,后来的 C99、C11 新增的特性并不多,只是在“打补丁”。在这博客中的一系列篇章中的内容虽然基于 C99 标准,但是绝大部分内容还是 C89 的,我会在 C89 和 C99 有差异的语法上给出重点说明。
(十一)C语言为什么会有那么多编译器?
通过上文C语言的标准中介绍可以知道,C语言没有一个官方的机构,也不属于哪个公司,他只有一个制定标准的委员会,任何其他组织或者个人都可以开发C语言的编译器,而这个编译器要遵守哪个C语言标准,是 100% 遵守还是部分遵守,并没有强制性的措施,也没有任何约束。
换句话说,各个厂商可以为了自己的利益、根据自己的喜好来开发编译器。
这就导致了一个棘手的问题,有的编译器遵守较新的C语言标准,有的编译器只能遵守较老的C语言标准,有的编译器还进行了很多扩展。比如:
- GCC、LLVM/Clang 更新非常及时,能够支持最新的 C11 标准(前提是你得使用最新版的编译器)。
- 微软编译器更新比较缓慢,迟迟不能支持新标准,例如 VC 6.0、VS2010 都在使用 C89 标准,VS2015 部分支持 C99 标准(其他版本的VS没有测试)。微软官方给出的答复是,最新的标准已经在 C++ 中支持了,C语言就没必要再重复了。
初学者经常会遇到这种情况,有些代码在微软编译器下能够正常运行,拿到 GCC 下就不行了,一堆报错信息;或者反过来,在 GCC 上能运行的代码在微软编译器下不能运行。这是因为不同的编译器支持不同的标准,并且每个编译器都进行了自己的扩展,假如你使用了微软编译器私有的扩展函数,那么拿到 GCC 下肯定是不支持的。
(十二)程序在安装过程中都做了什么?
我们知道,大部分软件都需要先安装才能使用,例如 QQ、360、迅雷等,要先从网上下载一个安装包,然后安装到计算机的C盘或者D盘等。大部分程序还会在开始菜单或者桌面上生成一个快捷方式,用户只要点击快捷方式,就可以启动软件。
那么,一个程序为什么要安装呢?下载后直接使用不可以吗?下面我们就来分析一下。
不同的操作系统,安装软件的方法虽然不一样,但基本原理是相同的,主要的思想就是将程序的二进制可执行文件拷贝到某个目录,设置一些路径。如果程序运行时需要一些库,将这些库拷贝到系统目录即可。
程序的安装基本上要经过下面四个步骤:
- 将程序的可执行文件从安装包所在的位置,拷贝到要安装的目录。
安装程序的时候,程序会给用户指定一个默认的安装目录,如果用户需要,也可以自定义安装,改变安装目录。一般所谓的“绿色软件”到此就安装结束了,可以使用了。
- 如果有必要,可以向系统目录拷贝一些动态链接库(DLL)。(可选操作)
有的程序,比如大型游戏,可能需要很多动态链接库(DLL)的支持,这时候程序可能会将这些 DLL 拷贝到系统库的默认目录,Win7 下一般拷贝到C:\Windows\System32\(读者不妨打开该目录,会看到很多 DLL 文件)。
有些程序用到的 DLL 文件不是系统必需的,只能由程序自己使用,这样放在系统目录里就不太合适,安装的软件多了,就会造成系统臃肿,所以这些 DLL 会被拷贝到程序的安装目录。
- 向系统注册表中写入相应的设置项,注册程序或者库的安装信息。(可选操作)
安装前,用户可能会对软件做一些设置,安装时,这些设置就会被写入注册表。另外,当安装程序将 DLL 文件拷贝到系统目录时,一些 DLL 还需要向系统注册,告诉系统我在这里,不然使用的时候可能会找不到。
- 在开始菜单或者桌面上位程序创建快捷方式。(可选操作)
创建快捷方式主要是为了用户使用方便,有了快捷方式,就不用再到安装目录去启动程序了。
由此可见,程序在安装前后并没有什么区别,只不过是进行了一些设置,有的设置是程序运行所必须的,有的是为了让用户更加方便。
(十三)C语言程序的错误和警告
一般C语言代码,在编译、链接和运行各个阶段都可能会出现问题。编译器只能检查编译和链接阶段出现的问题,而可执行程序已经脱离了编译器,运行阶段程序出现问题编译器是无能无力的。
如果我们编写正确,运行时会提示没有错误和警告。
**错误(Error)**表示程序不正确,不能正常编译、链接和运行,必须要纠正。
**警告(Warning)**表示可能发生错误(实际上未发生)或者代码不规范,但是程序能正常运行。有些警告可以忽略,有的要引起注意。
我们写的代码肯定会发生错误,一定要有分析错误的能力,这是一个合格的程序员必备的技能。
(十四)分析第一个C语言程序
注:本节涉及的很多概念后续会详细讲解,但还是希望读者多看几遍,做一个了解,有个概念。
我们以之前的一个完整代码为例:
#include <stdio.h>
int main()
{
puts("Hello, World!\n");
return 0;
}
函数的概念
先来看第 4 行代码,这行代码会在显示器上输出“C语言中文网”。前面我们已经讲过,puts 后面要带( ),字符串也要放在( )中。
在C语言中,有的语句使用时不能带括号,有的语句必须带括号。带括号的称为函数(Function)。
C语言提供了很多功能,例如输入输出、获得日期时间、文件操作等,我们只需要一句简单的代码就能够使用。但是这些功能的底层都比较复杂,通常是软件和硬件的结合,还要要考虑很多细节和边界,如果将这些功能都交给程序员去完成,那将极大增加程序员的学习成本,降低编程效率。
好在C语言的开发者们为我们做了一件好事,他们已经编写了大量代码,将常见的基本功能都完成了,我们可以直接拿来使用。但是现在问题来了,那么多代码,如何从中找到自己需要的呢?一股脑将所有代码都拿来显然是非常不明智的。
这些代码,早已被分门别类地放在了不同的文件中,并且每一段代码都有唯一的名字。使用代码时,只要在对应的名字后面加上( )就可以。这样的一段代码能够独立地完成某个功能,一次编写完成后可以重复使用,被称为函数(Function)。读者可以认为,函数就是一段可以重复使用的代码。
函数的一个明显特征就是使用时必须带括号( ),必要的话,括号中还可以包含待处理的数据。例如puts(“C语言中文网”)就使用了一段具有输出功能的代码,这段代码的名字是 puts,“C语言中文网” 是要交给这段代码处理的数据。使用函数在编程中有专业的称呼,叫做函数调用(Function Call)。
如果函数需要处理多个数据,那么它们之间使用逗号,分隔,例如:
pow(10, 2);
该函数用来求10的2次方。
需要注意的是,C语言中的函数和数学中的函数不是同一个概念,不要拿两者对比。函数的英文名称是 Function,它还有“功能”的意思。大陆将 Function 翻译为“函数”,而*地区翻译为“函式”,读者要注意区分。
自定义函数和main函数
C语言自带的函数称为库函数(Library Function)。库(Library)是编程中的一个基本概念,可以简单地认为它是一些列函数的集合,在磁盘上往往是一个文件夹。C语言自带的库称为标准库(Standard Library),其他公司或个人开发的库称为第三方库(Third-Party Library)。
除了库函数,我们还可以编写自己的函数,拓展程序的功能。自己编写的函数称为自定义函数。自定义函数和库函数在编写和使用方式上完全相同,只是由不同的机构来编写。
示例中第2~6行代码就是我们自己编写的一个函数。main 是函数的名字,( ) 表明这是函数定义,{ } 之间的代码是函数要实现的功能。
函数可以接收待处理的数据,同样可以将处理结果告诉我们;使用return可以告知处理结果。示例中第5行代码表明,main 函数的处理结果是整数 0。return 可以翻译为“返回”,所以函数的处理结果被称为返回值(Return Value)。
第2行代码中,int 是 integer 的简写,意为“整数”。它告诉我们,函数的返回值是整数。
需要注意的是,示例中的自定义函数必须命名为 main。C语言规定,一个程序必须有且只有一个 main 函数。main 被称为主函数,是程序的入口函数,程序运行时从 main 函数开始,直到 main 函数结束(遇到 return 或者执行到函数末尾时,函数才结束)。
也就是说,没有 main 函数程序将不知道从哪里开始执行,运行时会报错。
综上所述:第2~6行代码定义了主函数 main,它的返回值是整数 0,程序将从这里开始执行。main 函数的返回值在程序运行结束时由系统接收。
关于C语言函数的更多内容,我们后续将单独详细讲解
头文件的概念
还有最后一个问题,示例中第1行的#include <stdio.h>是什么意思呢?
C语言开发者们编写了很多常用函数,并分门别类的放在了不同的文件,这些文件就称为头文件(header file)。每个头文件中都包含了若干个功能类似的函数,调用某个函数时,要引入对应的头文件,否则编译器找不到函数。
实际上,头文件往往只包含函数的说明,也就是告诉我们函数怎么用,而函数本身保存在其他文件中,在链接时才会找到。对于初学者,可以暂时理解为头文件中包含了若干函数。
引入头文件使用#include命令,并将文件名放在< >中,#include 和 < > 之间可以有空格,也可以没有。
头文件以.h为后缀,而C语言代码文件以.c为后缀,它们都是文本文件,没有本质上的区别,#include 命令的作用也仅仅是将头文件中的文本复制到当前文件,然后和当前文件一起编译。你可以尝试将头文件中的内容复制到当前文件,那样也可以不引入头文件。
.h中代码的语法规则和.c中是一样的,你也可以#include <xxx.c>,这是完全正确的。不过实际开发中没有人会这样做,这样看起来非常不专业,也不规范。
总结
本节开头的示例是一个C语言程序的基本结构,我们不妨整理一下思路,从整体上再分析一遍:
第1行引入头文件 stdio.h,这是编程中最常用的一个头文件。头文件不是必须要引入的,我们用到了 puts 函数,所以才引入 stdio.h。例如下面的代码完全正确:
int main() {
return 0;
}
我们没有调用任何函数,所以不必引入头文件。
第2行开始定义主函数 main。main 是程序的入口函数,一个C程序必须有 main 函数,而且只能有一个。
第4行调用 puts 函数向显示器输出字符串。
第5行是 main 函数的返回值。程序运行正确一般返回 0。