C语言入门教程,C语言学习教程(第二部分:C语言初探)二

发布时间:2024年01月10日

十、C语言的三套标准:C89、C99和C11

我们今天使用的 Windows、Linux、Mac OS 等操作系统都是由一种叫做 Unix 的系统演化而来。Unix 作为80年代主流的操作系统,是整个软件工业的基础,是现代操作系统的开山鼻祖,C语言就是为 Unix 而生的。

Unix 和C语言的开发者是同一人,名字叫丹尼斯·里奇(Dennis MacAlistair Ritchie)

丹尼斯·里奇(Dennis MacAlistair Ritchie)

?C语言和 Unix 之父——丹尼斯·里奇

2011年10月12日(北京时间为10月13日),丹尼斯·里奇去世,享年70岁。

1、C语言的诞生

1967年,26岁的丹尼斯·里奇进入贝尔实验室开发 Unix,并于 1969 年圣诞节前推出第一个试运行版本。这个时候的 Unix 是用汇编语言写的,移植性非常差,要想让 Unix 运行在不同型号的机器上,就得针对每个型号的机器重写一遍操作系统,这显然是一个不可能完成的任务。

为了提高通用性和开发效率,丹尼斯·里奇决定发明一种新的编程语言——C语言。紧接着,丹尼斯·里奇就用C语言改写了 Unix 上的C语言编译器,他的同事汤姆森则使用C语言重写了 Unix,使它成为一种通用性强、移植简单的操作系统,从此开创了计算机编程史上的新篇章,C语言也成为了操作系统专用语言。

2、C89 标准

到了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 的内容。

2、C99 标准

在 ANSI C 标准确立之后,C语言的规范在很长一段时间内都没有大的变动。1995 年C程序设计语言工作组对C语言进行了一些修改,增加了新的关键字,编写了新的库,取消了原有的限制,并于 1999 年形成新的标准——ISO/IEC 9899:1999 标准,通常被成为?C99

但是这个时候的C语言编译器基本已经成熟,各个组织对 C99 的支持所表现出来的兴趣不同。当 GCC 和其它一些商业编译器支持 C99 的大部分特性的時候,微软和 Borland 却似乎对此不感兴趣,或者说没有足够的资源和动力来改进编译器,最终导致不同的编译器在部分语法上存在差异。

例如,ANSI C 规定,局部变量要在函数开头定义,而 C99 取消了这个限制,变量可以在任意位置定义,我们将在《C语言变量的定义位置以及初始值》一节中详细介绍。

3、C11 标准

C11 标准由国际标准化组织(ISO)和国际电工委员会(IEC) 旗下的C语言标准委员会于 2011 年底正式发布,支持此标准的主流C语言编译器有 GCC、LLVM/Clang、Intel C++ Compile 等。

C11 标准主要增加了以下内容:

  • 增加了安全函数,例如 gets_s()、fopen_s() 等;
  • 增加了?<threads.h> 头文件以支持多线程;
  • 增加了?<uchar.h> 头文件以支持 Unicode 字符集;
  • 以及其它一些细节。

2018 年,ISO/IEC 又发布了 C11 标准的修正版,称为 C17 或者 C18 标准。和 C11 标准相比,C17 并没有添加新的功能和语法特性,仅仅修正了 C11 标准中已知的一些缺陷。

截至到 2022 年 7 月份,新的 C2x 标准尚未发布,最新的 C 语言标准仍是 C17。

4、总结

现有的教程(包括书籍、视频、大学课程等)大都是针对 C89 编写的,这是C语言的核心,后来的 C99、C11 新增的特性并不多,只是在“打补丁”。本教程虽然基于 C99 标准,但是绝大部分内容还是 C89 的,我会在 C89 和 C99 有差异的语法上给出重点说明。


十一、C语言为什么有那么多编译器?

通过上节《C语言的三套标准:C89、C99和C11》的介绍可以发现,C语言并没有一个官方机构,也不属于哪个公司,它只有一个制定标准的委员会,任何其他组织或者个人都可以开发C语言的编译器,而这个编译器要遵守哪个C语言标准,是 100% 遵守还是部分遵守,并没有强制性的措施,也没有任何约束。

换句话说,各个厂商可以为了自己的利益、根据自己的喜好来开发编译器。

这就导致了一个棘手的问题,有的编译器遵守较新的C语言标准,有的编译器只能遵守较老的C语言标准,有的编译器还进行了很多扩展。比如:

  • GCC、LLVM/Clang 更新非常及时,能够支持最新的 C11 标准(前提是你得使用最新版的编译器)。
  • 微软编译器更新比较缓慢,迟迟不能支持新标准。?例如 VC 6.0、VS2010 都在使用 C89 标准,VS2015 部分支持 C99 标准,直到 VS2019 才部分支持 C11 和 C17 标准。微软官方给出的答复是,最新的标准已经在 C++ 中支持了,C语言就没必要再重复了。

初学者经常会遇到这种情况,有些代码在微软编译器下能够正常运行,拿到 GCC 下就不行了,一堆报错信息;或者反过来,在 GCC 上能运行的代码在微软编译器下不能运行。这是因为不同的编译器支持不同的标准,并且每个编译器都进行了自己的扩展,假如你使用了微软编译器私有的扩展函数,那么拿到 GCC 下肯定是不支持的。


十二、程序安装是怎么回事?

我们知道,大部分软件都需要先安装才能使用,例如 QQ、360、迅雷等,要先从网上下载一个安装包,然后安装到计算机的C盘或者D盘等。大部分程序还会在开始菜单或者桌面上生成一个快捷方式,用户只要点击快捷方式,就可以启动软件。

那么,一个程序为什么要安装呢?下载后直接使用不可以吗?下面我们就来分析一下。

不同的操作系统,安装软件的方法虽然不一样,但基本原理是相同的,主要的思想就是将程序的二进制可执行文件拷贝到某个目录,设置一些路径。如果程序运行时需要一些库,将这些库拷贝到系统目录即可。

程序的安装基本上要经过下面四个步骤:

1) 将程序的可执行文件从安装包所在的位置,拷贝到要安装的目录。

安装程序的时候,程序会给用户指定一个默认的安装目录,如果用户需要,也可以自定义安装,改变安装目录。一般所谓的“绿色软件”到此就安装结束了,可以使用了。

2) 如果有必要,可以向系统目录拷贝一些动态链接库(DLL)。(可选操作)

有的程序,比如大型游戏,可能需要很多动态链接库(DLL)的支持,这时候程序可能会将这些 DLL 拷贝到系统库的默认目录,Win7 下一般拷贝到C:\\Windows\System32\读者不妨打开该目录,会看到很多 DLL 文件)。

有些程序用到的 DLL 文件不是系统必需的,只能由程序自己使用,这样放在系统目录里就不太合适,安装的软件多了,就会造成系统臃肿,所以这些 DLL 会被拷贝到程序的安装目录。

3) 向系统注册表中写入相应的设置项,注册程序或者库的安装信息。(可选操作)

安装前,用户可能会对软件做一些设置,安装时,这些设置就会被写入注册表。另外,当安装程序将 DLL 文件拷贝到系统目录时,一些 DLL 还需要向系统注册,告诉系统我在这里,不然使用的时候可能会找不到。

4) 在开始菜单或者桌面上位程序创建快捷方式。(可选操作)

创建快捷方式主要是为了用户使用方便,有了快捷方式,就不用再到安装目录去启动程序了。

由此可见,程序在安装前后并没有什么区别,只不过是进行了一些设置,有的设置是程序运行所必须的,有的是为了让用户更加方便。


十三、制作安装包,让用户安装程序

经过多年的发展,Windows 安装包的制作技术已经非常成熟,有不少现成的工具,无需我们自己编写代码,例如 Inno Setup、NSIS、Advanced Installer、Setup Factory、Smart Install Maker、Nullsoft、InstallShield,Advanced Installer 等。

软件安装的过程大部分是文件拷贝的过程,如果不需要写入注册表,不需要向不同的目录拷贝 DLL,那么使用 WinRAR 也可以制作简单的安装包,大家可以自行谷歌或百度。这里我们教大家使用 VS2022 制作安装包。

?

图 1 制作安装包的 C 语言项目

以图 1 中的 C 语言项目为例,编译后会生成一个名为“Demo.exe”的可执行文件,在 Demo 项目的本地文件夹中可以找到它,利用 VS2022 将 Demo.exe 制作成安装包需要经历以下几个步骤。

1、安装Installer Projects

Installer Projects 是 VS2022 自带的一款制作安装包的插件,借助它,在 VS 中编写的程序可以打包成标准的 Windows 安装包。

安装 Installer Projects 的过程很简单,在 VS2022 菜单栏中依次选择“扩展 -> 管理扩展”,会弹出如下的窗口:

?

图 2 安装 Installer Projects 组件

搜索框中手动输入“Visual Studio Installer Projects”找到 Installer Projects 组件,点击“下载”。整个安装的过程非常简单,这里不再给出具体图示。

安装完成后,重启 VS2022,就可以使用 Installer Projects 组件了。

2、新建制作安装包的项目

在 Demo 项目所在的解决方案中,再新建一个制作安装包的项目(也可以单独创建一个制作安装包的项目):

?

图 3 新建制作安装包的项目

在弹出的如下窗口中,选择创建“Setup Project”项目:

?

图 4 创建 Setup Project 项目

注意,如果找不到 Setup Project 项目,表明你的 VS2022 尚未安装 Installer Projects,返回第 1 步安装成功后再继续往下进行。

在弹出的如下窗口中,可以自定义项目的名称和存储位置:

?

图 5 自定义项目名称和存储位置

创建完成后,当前解决方案中就包含了两个项目,一个是 Demo 项目,另一个是 DemoSetup 项目:

?图 6 一个解决方案中包含两个项目

3、制作安装包

鼠标右击图 6 中的 DemoSetup 项目名,依次选择“View -> 文件系统”,可以看到 3 个文件夹:

  • Application Folder(应用程序文件夹):放置制作安装包所需的所有文件,包括可执行文件、库文件、icon 图标等;
  • User's Desktop(用户的桌面):指定用户安装应用程序完成后,桌面上需要放置的内容,通常是可执行程序的快捷方式;
  • User's Programs Menu(用户的程序菜单):指定用户安装应用程序完成后,开始菜单中需要显示的内容,比如可执行程序和卸载程序的快捷方式。

(1) 应用程序文件夹中添加文件

在应用程序文件夹处单击鼠标右键选择“添加(Add) -> 文件夹(Floder)”,如下图所示:

?

图 7 应用程序文件夹中添加文件夹

我们不妨将文件夹命名为 Demo。这个文件夹就是程序安装后所在安装路径下生成的、包含程序相关组件的文件夹。例如,用户选择将程序安装到 D:\Program Files\ 目录,那么安装时会先在该目录创建一个名称为 Demo 的新文件夹,再将程序的所有组件拷贝到 Demo 中,最终程序的所有文件是在 D:\Program Files\Demo\ 目录下。

接下来,向 Demo 文件夹中添加程序要用到的所有文件,如下图所示:

?

图 8 应用程序文件夹中添加文件

添加好以后如下图所示:

?

图 9 添加好的文件

Demo.exe 是我们编译好的可执行文件,demo.ico 和 uninstall.ico 是图标,用来创建快捷方式。程序图标必须是 .ico 格式,可以通过 jpg、png 等常见图片格式在线转换。文章最后会给出两个图标的下载地址。

制作安装包之前,可以将程序使用到的所有文件都拷贝到一个目录下,这样就可以一次性添加到 Demo。

增加卸载功能

如果想为安装包增加卸载功能的话,还需要将C:\Windows\System32\msiexec.exe也添加到 CDemo 中。

(2)桌面和开发菜单中添加快捷方式

快捷方式存在于两个地方,分别是桌面和开始菜单,用户的程序菜单用来存放开始菜单中的快捷方式,用户的桌面用来存放桌面上的快捷方式。

在 "Demo.exe" 上单击鼠标右键选择创建 Demo.exe 的快捷方式,如下图所示:

?

图 10 添加 Demo.exe 的快捷方式

鼠标右击新生成的快捷方式(Shortcut to Demo.exe),选择“属性窗口”,下图所示:

?

图 11 快捷方式的属性

Name 表示快捷方式的名称,一般是程序名,这里重置为 Demo;Description 表示对快捷方式的说明,也就是鼠标悬浮时显示的文本;Target 表示快捷方式指向哪个可执行程序;Icon 表示快捷方式的图标,将其设置为 demo.ico 。

按照同样的方式为 msiexec.exe 也生成快捷方式,并将 Name 设置为“卸载Demo”,将 Icon 设置为“uninstall.ico”。

在“用户的程序菜单”下新建文件夹 Demo,将两个快捷方式移动到此文件夹下。然后再为 demo.exe 创建一个快捷方式,并移动到“用户的桌面”下。

这样,程序安装后在桌面和开始菜单中都有快捷方式,都可以启动了。

(3)实现卸载功能(可选)

上面我们虽然添加了系统自带的卸载程序,并为卸载程序创建了快捷方式,但目前依然无法实现卸载功能,因为还不知道要卸载哪个程序。

在图 11 属性面板的基础上,鼠标点击项目名 DemoSetup,可以看到和此项目有关的属性,如下图所示:

?图 12 项目属性面板

ProductCode 是产品代码,即产品 ID,我们需要将它告诉卸载程序。每个程序的 ID 都不同,有了它,卸载程序才知道卸载哪一个软件。本例中,程序 ID 为 {FF9566C2-47D6-4B1F-95D4-67C4EDD2F7D6}。

在卸载程序 msiexec.exe 快捷方式的属性面板中,将 Arguments 的值设置为/x{FF9566C2-47D6-4B1F-95D4-67C4EDD2F7D6},如下图所示:

?图 13 实现卸载功能

这样,卸载程序就知道卸载哪个软件了。

(4)选择系统必备

在项目名称 DemoSetup 上点击鼠标右键,选择“属性”,弹出如下的对话框:

?

图 14 设置系统必备

点击“Prerequisites...”(系统必备),弹出如下窗口:

?

图 15 自选系统必备的组件

可以在这里选择程序需要的系统组件。我们的程序不需要任何组件,所以将【创建用于安装系统必备组件的安装程序】前的对勾取消,然后点击“确定”按钮,就设置完成了。

至此,我们的安装包就编辑完成了。

(5) 生成安装包

鼠标右击 DemoSetup 项目名,选择“生成”或者“重新生成”:

?

图 16 生成安装包

生成项目后,可以在 Debug 目录下看到 CDemoSetup.msi 文件,这就是我们制作好的安装包,双击运行,就可以安装我们的程序了。

安装完成后,就可以在开始菜单和桌面上看到快捷方式了,如下图所示:

?图 17 安装好的应用程序

点击 Demo,可以看到程序的运行结果;点击“卸载 Demo”,可以卸载 Demo 应用程序。

程序图标下载地址:http://pan.baidu.com/s/1pK0fwIR? 提取密码:iux9


十四、C语言程序的错误和警告

一段C语言代码,在编译、链接和运行的各个阶段都可能会出现问题。编译器只能检查编译和链接阶段出现的问题,而可执行程序已经脱离了编译器,运行阶段出现问题编译器是无能为力的。

如果我们编写的代码正确,运行时会提示没有错误(Error)和警告(Warning),如下图所示:

Dev C++ 的提示?

图1:Dev?C++?的提示

VC 6.0 的提示

?图2:VC 6.0 的提示

C-Free 5.0 的提示

?图3:C-Free 5.0 的提示

对于 VS、GCC、Xcode 等,如果代码没有错误,它们只会显示“生成成功”,不会显示“0个错误,0个警告”,只有代码真的出错了,它们才会显示具体的错误信息。

错误(Error)表示程序不正确,不能正常编译、链接或运行,必须要纠正。

警告(Warning)表示可能会发生错误(实际上未发生)或者代码不规范,但是程序能够正常运行,有的警告可以忽略,有的要引起注意。

错误和警告可能发生在编译、链接、运行的任何时候。

例如,puts("C语言中文网")最后忘记写分号;,就会出现错误,如下图所示:

VS2015 的错误提示?

图4:VS2015 的错误提示

Dev C++ 的错误提示

?图5:Dev C++ 的错误提示

VC 6.0 的错误提示?

图6:VC 6.0 的错误提示

C-Free 5.0 的错误提示

?图7:C-Free 5.0 的错误提示

可以看出,C-Free 的错误提示信息比较少,不方便程序员纠错。VC 和 VS 的错误信息类似,只是中英文的差别。

下图分析了 VC 6.0 的错误信息:

错误信息说明?

图8:错误信息说明

翻译:源文件?E:\cDemo\hello.c 第5行发生了语法错误,错误代码是?C2143,原因是?'return' 前面丢失了 ';'。

我敢保证,你写的代码肯定会发生错误,一定要有分析错误的能力,这是一个合格的程序员必备的技能。


十五、分析第一个C语言程序

前面我们给出了一段最简单的C语言代码,并演示了如何在不同的平台下进行编译,这节我们来分析一下这段代码,让读者有个整体的认识。代码如下:

#include <stdio.h>

int main()

{

???????? puts("C语言中文网");

???????? return 0;

}

1、函数的概念

先来看第 4 行代码,这行代码会在显示器上输出“C语言中文网”。前面我们已经讲过,puts 后面要带( ),字符串也要放在( )中。

在C语言中,有的语句使用时不能带括号,有的语句必须带括号。带括号的称为函数(Function)

C语言提供了很多功能,例如输入输出、获得日期时间、文件操作等,我们只需要一句简单的代码就能够使用。但是这些功能的底层都比较复杂,通常是软件和硬件的结合,还要要考虑很多细节和边界,如果将这些功能都交给程序员去完成,那将极大增加程序员的学习成本,降低编程效率。

好在C语言的开发者们为我们做了一件好事,他们已经编写了大量代码,将常见的基本功能都完成了,我们可以直接拿来使用。但是现在问题来了,那么多代码,如何从中找到自己需要的呢?一股脑将所有代码都拿来显然是非常不明智的。

这些代码,早已被分门别类地放在了不同的文件中,并且每一段代码都有唯一的名字。使用代码时,只要在对应的名字后面加上( )就可以。这样的一段代码能够独立地完成某个功能,一次编写完成后可以重复使用,被称为函数(Function)。读者可以认为,函数就是一段可以重复使用的代码。

函数的一个明显特征就是使用时必须带括号( ),必要的话,括号中还可以包含待处理的数据。例如puts("C语言中文网")就使用了一段具有输出功能的代码,这段代码的名字是?puts,"C语言中文网"?是要交给这段代码处理的数据。使用函数在编程中有专业的称呼,叫做函数调用(Function Call)

如果函数需要处理多个数据,那么它们之间使用逗号,分隔,例如:

pow(10, 2);

该函数用来求10的2次方。

需要注意的是,C语言中的函数和数学中的函数不是同一个概念,不要拿两者对比。函数的英文名称是 Function,它还有“功能”的意思。大陆将 Function 翻译为“函数”,而台湾地区翻译为“函式”,读者要注意区分。

2、自定义函数和main函数

C语言自带的函数称为库函数(Library Function)库(Library)是编程中的一个基本概念,可以简单地认为它是一些列函数的集合,在磁盘上往往是一个文件夹。C语言自带的库称为标准库(Standard Library),其他公司或个人开发的库称为第三方库(Third-Party Library)

关于库的概念,我们已在《不要这样学习C语言,这是一个坑!》中进行了详细介绍。

除了库函数,我们还可以编写自己的函数,拓展程序的功能。自己编写的函数称为自定义函数。自定义函数和库函数在编写和使用方式上完全相同,只是由不同的机构来编写。

示例中第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语言函数详解(包括声明、定义、使用等)》一章中详细讲解,这里不再展开讨论。

有的教材中将 main 函数写作:

void main()

{

???????? // Some Code...

}

这在 VC6.0 下能够通过编译,但在 C-Free、GCC中却会报错,因为这不是标准的 main 函数的写法,大家不要被误导,最好按照示例中的格式来写。

3、头文件的概念

还有最后一个问题,示例中第1行的#include <stdio.h>是什么意思呢?

C语言开发者们编写了很多常用函数,并分门别类的放在了不同的文件,这些文件就称为头文件(header file)。每个头文件中都包含了若干个功能类似的函数,调用某个函数时,要引入对应的头文件,否则编译器找不到函数。

实际上,头文件往往只包含函数的说明,也就是告诉我们函数怎么用,而函数本身保存在其他文件中,在链接时才会找到。对于初学者,可以暂时理解为头文件中包含了若干函数。

引入头文件使用#include命令,并将文件名放在< >中,#include 和 < > 之间可以有空格,也可以没有。

头文件以.h为后缀,而C语言代码文件以.c为后缀,它们都是文本文件,没有本质上的区别,#include 命令的作用也仅仅是将头文件中的文本复制到当前文件,然后和当前文件一起编译。你可以尝试将头文件中的内容复制到当前文件,那样也可以不引入头文件。

.h中代码的语法规则和.c中是一样的,你也可以#include <xxx.c>,这是完全正确的。不过实际开发中没有人会这样做,这样看起来非常不专业,也不规范。

较早的C语言标准库包含了15个头文件,stdio.h 和 stdlib.h 是最常用的两个:

  • stdio 是 standard input output 的缩写,stdio.h 被称为“标准输入输出文件”,包含的函数大都和输入输出有关,puts() 就是其中之一。
  • stdlib 是 standard library 的缩写,stdlib.h 被称为“标准库文件”,包含的函数比较杂乱,多是一些通用工具型函数,system() 就是其中之一。

4、最后的总结

初学编程,有很多基本概念需要了解,本节就涉及到很多,建议大家把上面的内容多读几遍,必将有所收获。

本节开头的示例是一个C语言程序的基本结构,我们不妨整理一下思路,从整体上再分析一遍:
1) 第1行引入头文件 stdio.h,这是编程中最常用的一个头文件。头文件不是必须要引入的,我们用到了 puts 函数,所以才引入 stdio.h。例如下面的代码完全正确:

int main()

{

???????? return 0;

}

我们没有调用任何函数,所以不必引入头文件。

2) 第2行开始定义主函数 main。main 是程序的入口函数,一个C程序必须有 main 函数,而且只能有一个。

3) 第4行调用 puts 函数向显示器输出字符串。

4) 第5行是 main 函数的返回值。程序运行正确一般返回 0。


十六、C语言代码中的空白符

空格、制表符、换行符等统称为空白符(space character),它们只用来占位,并没有实际的内容,也显示不出具体的字符。

制表符分为水平制表符和垂直制表符,它们的 ASCII 编码值分别是 9 和 11。

  • 垂直制表符在现代计算机中基本不再使用了,也没法在键盘上直接输入,它已经被换行符取代了。
  • 水平制表符相当于四个空格,对于大部分编辑器,按下 Tab 键默认就是输入一个水平制表符;如果你进行了个性化设置,按下 Tab 键也可能会输入四个或者两个空格。

对于编译器,有的空白符会被忽略,有的却不能。请看下面几种 puts 的写法:

#include<stdio.h>

int main()

{

???????? puts("C语言");

???????? puts("中文网");

???????? puts

????????("C语言中文网");

????????puts

???????? (

???????? "C语言中文网"

???????? )

????????;????????

????????puts ???????? ("C语言中文网");

???????? puts???????? ( ???????? "C语言中文网" ???????? )???????? ;

???????? return 0;

}

运行结果:

看到输出结果,说明代码没有错误,以上几种 puts 的用法是正确的。puts()之间、" "()之间可以有任意的空白符,它们会被编译器忽略,编译器不认为它们是代码的一部分,它们的存在只是在编辑器中呈现一定的格式,让程序员阅读方便。

需要注意的是,由" "包围起来的字符串中的空白符不会被忽略,它们会被原样输出到控制台上;并且字符串中间不能换行,否则会产生编译错误。请看下面的代码:

#include<stdio.h>

int main()

{

???????? puts("C语? ?言? ?中文网");

???????? puts("C语言

???????? 中文网");

???????? return 0;

}

第 5~6 行代码是错误的,字符串必须在一行内结束,不能换行。把这两行代码删除,运行结果为:



程序员要善于利用空白符:缩进(制表符)和换行可以让代码结构更加清晰,空格可以让代码看起来不那么拥挤。专业的程序员同样追求专业的代码格式,大家在以后的学习中可以慢慢体会。


十七、彩色版的C语言,让文字更漂亮

对于初学者来说,本节涉及到的代码比较复杂,名字也不容易记住,大家只需要把代码复制到编译器,看一下运行效果就可以。本节重在演示C语言能做什么,而不是教授大家知识点(这些知识点也不是C语言的重点),所以,不理解的就放过吧,不会影响后面的学习。

C语言不总是“黑底白字”,它也可以是彩色的,可以调用Windows.h文件下的SetConsoleTextAttribute函数改变文字和背景颜色。

调用形式为:

SetConsoleTextAttribute( HANDLE hConsoleOutput, WORD wAttributes );

hConsoleOutput表示控制台缓冲区句柄,可以通过GetStdHandle(STD_OUTPUT_HANDLE)来获得;wAttributes表示文字颜色和背景颜色。

WORDwindows.h中定义,等同于unsigned short,使用低4位表示文字(前景)颜色,高4位表示文字背景颜色,所以它的取值为xx。x为一位16进制数,即0~F都可以使用,可以随意组合。

0~F 分别代表的颜色如下:

0 = 黑色 ? ?8 = 灰色 ? ?1 = 淡蓝 ? ? ?9 = 蓝色
2 = 淡绿 ? ?A = 绿色 ? ?3 = 湖蓝 ? ? ?B = 淡浅绿 ?
C = 红色 ? ?4 = 淡红 ? ?5 = 紫色????? D = 淡紫 ?
6 = 黄色 ? ?E = 淡黄 ? ?7 = 白色????? F = 亮白

例如,将背景设置为淡绿色,文字设置为红色

#include <stdio.h>

#include <windows.h>

int main(){

????????HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); ????????SetConsoleTextAttribute(hConsole, 0x2C );

????????puts("C语言中文网");

????????return 0;

}

运行结果:

如果只希望设置文字颜色,背景保持黑色,那么也可以只给出一位16进制数,例如:

SetConsoleTextAttribute(hConsole, 0xC ); //将文字颜色设置为红色 SetConsoleTextAttribute(hConsole, 0xF ); //将文字颜色设置为白色

再来看一个例子:

#include <stdio.h>

#include <windows.h>

int main(){

???????? HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

???????? SetConsoleTextAttribute(hConsole, 0xC );

???????? puts("红色文字");

???????? SetConsoleTextAttribute(hConsole, 0xF );

???????? puts("白色文字");

???????? SetConsoleTextAttribute(hConsole, 2 );

???????? puts("淡绿色文字");

???????? return 0;

}

运行结果:


十八、一个真正带界面的C语言程序

对于初学者来说,本节涉及到的代码比较复杂,名字也不容易记住,大家只需要把代码复制到编译器,看一下运行效果就可以。本节重在演示C语言能做什么,而不是教授大家知识点(这些知识点也不是C语言的重点),所以,不理解的就放过吧,不会影响后面的学习。

C语言不仅可以编写“黑屏”的控制台程序,还可以编写拥有漂亮界面的Windows程序(GUI程序),只是绝大多数C语言教程都没有告诉你而已,让你以为C语言程序只能是“黑乎乎”的。

开发 GUI 程序需要选择的项目类型是“Win32项目”,其他操作和控制台程序一样。

第一个带界面的C语言程序:

#include <windows.h>

int WINAPI WinMain(

????????HINSTANCE hInstance,

????????HINSTANCE hPrevInstance,

????????LPSTR lpCmdLine,

????????int nCmdShow

){

????????int nSelect = MessageBox(NULL, "你好,欢迎来到C语言中文网!", "Welcome", MB_OKCANCEL | MB_ICONEXCLAMATION);

????????return 0;

}

运行结果:

你看,弹出一个带界面的欢迎窗口。控制台程序以main为入口函数,Windows程序以WinMain为入口函数。

上面的例子仅仅是一个提示框,下面我们来创建一个真正的窗口:

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(

????????HINSTANCE hInstance,

????????HINSTANCE hPrevInstance,

????????PSTR szCmdLine,

????????int iCmdShow

){

???????? static TCHAR szAppName[] = TEXT("HelloWin");

???????? HWND? ? ? ? ? ?hwnd;

???????? MSG????????? ? ? msg;

???????? WNDCLASS? wndclass;

? ? ? ? ?wndclass.style? ? ? ? ? ? ? ? ? = CS_HREDRAW | CS_VREDRAW;

? ? ? ? ?wndclass.lpfnWndProc? ? ?= WndProc;

???????? wndclass.cbClsExtra? ? ?? ?= 0;

???????? wndclass.cbWndExtra? ? ? = 0;

???????? wndclass.hInstance? ? ? ? ??= hInstance ;

? ? ? ? ?wndclass.hIcon? ? ? ? ? ? ? ?? = LoadIcon (NULL, IDI_APPLICATION);

???????? wndclass.hCursor? ? ? ? ? ?? = LoadCursor (NULL, IDC_ARROW);

???????? wndclass.hbrBackground? = (HBRUSH) GetStockObject (WHITE_BRUSH);

???????? wndclass.lpszMenuName??= NULL ;

???????? wndclass.lpszClassName? = szAppName ;

???????? if( !RegisterClass(&wndclass) ){

????????????????MessageBox(

????????????????????????NULL,

????????????????????????TEXT("This program requires Windows NT!"),

????????????????????????szAppName,

????????????????????????MB_ICONERROR

????????????????);

????????return 0 ;

????????????????}

????????// creation parameters

????????hwnd = CreateWindow(

????????????????szAppName, // window class name

????????????????TEXT("Welcome"), // window caption

????????????????WS_OVERLAPPEDWINDOW, // window style

????????????????CW_USEDEFAULT, // initial x position

????????????????CW_USEDEFAULT, // initial y position

????????????????500, // initial x size

????????????????300, // initial y size

????????????????NULL, // parent window handle

????????????????NULL, // window menu handle

????????????????hInstance, // program instance handle

????????????????NULL

????????);

????????ShowWindow (hwnd, iCmdShow) ;

????????UpdateWindow (hwnd) ;

????????while( GetMessage(&msg, NULL, 0, 0) ){

????????????????TranslateMessage(&msg);

????????????????DispatchMessage (&msg);

????????}

????????return msg.wParam;

????????}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){

????????HDC hdc;

????????PAINTSTRUCT ps;

????????RECT rect;

????????switch (message){

????????????????case WM_PAINT:

????????????????????????hdc = BeginPaint (hwnd, &ps) ;

????????????????????????GetClientRect (hwnd, &rect) ;

????????????????????????DrawText(

????????????????????????????????hdc,

????????????????????????????????TEXT("你好,欢迎来到C语言中文网"),

????????????????????????????????-1,

????????????????????????????????&rect,

????????????????????????????????DT_SINGLELINE | DT_CENTER | DT_VCENTER

????????????????????????) ;

????????????????????????EndPaint (hwnd, &ps) ;

????????????????????????return 0 ;

????????????????case WM_DESTROY:

????????????????????????PostQuitMessage(0) ;

????????????????????????return 0 ;

????????}

????????return DefWindowProc(hwnd, message, wParam, lParam) ;

}

运行结果:

你看,创建一个简单的窗口就需要这么多C语言代码,根本记不住,学起来太吃力了。

实际开发中,我们一般不使用这种粗暴的方式创建GUI程序,而是使用 C/C++ 的界面库,它们已经把这些繁杂琐碎的代码给封装好了,让我们“站在了巨人的肩上”,例如针对C语言的 GTK,针对C++的 MFC、Qt、Duilib 等。

文章来源:https://blog.csdn.net/sinat_41942180/article/details/135065688
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。