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

十、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 等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/306259.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

html的全选反选

一、实验题目 html实现选择框的全选和反选 二、实验代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>全选和反选</title></head><body><ul>兴趣爱好</ul><input id"all"…

国芯科技荣膺高工智能汽车“年度车规MCU高成长供应商”,加速产品精准化系列化布局

2023年12月13—15日&#xff0c;2023&#xff08;第七届&#xff09;高工智能汽车年会在上海召开&#xff0c;大会以“寻找拐点”为主题&#xff0c;通过超80场主题演讲及多场圆桌对话&#xff0c;为智能汽车赛道参与者「备战2024」提供全方位的决策支持。 作为汽车电子芯片领…

网络安全漏洞的常见类型

网络犯罪分子可以利用的常见网络安全漏洞包括凭证薄弱、缺乏数据加密、配置错误、软件过时和零日漏洞。这些漏洞通常会导致网络攻击&#xff0c;绕过组织的安全措施并窃取机密数据。组织需要识别并缓解这些漏洞&#xff0c;以防止安全漏洞。 继续阅读以了解有关网络安全漏洞的…

Apache ECharts | 一个数据可视化图表库

文章目录 1、简介1.1、主要特点1.2、使用场景 2、安装方式一&#xff1a;从下载的源代码或编译产物安装方法二&#xff1a;从 npm 安装方法三&#xff1a;⭐定制安装echarts.js 3、使用 官网&#xff1a; 英语&#xff1a;https://echarts.apache.org/en/index.html 中文&a…

ChatGLM2-6B 大语言模型本地搭建

ChatGLM模型介绍&#xff1a; ChatGLM2-6B 是清华 NLP 团队于不久前发布的中英双语对话模型&#xff0c;它具备了强大的问答和对话功能。拥有最大32K上下文&#xff0c;并且在授权后可免费商用&#xff01; ChatGLM2-6B的6B代表了训练参数量为60亿&#xff0c;同时运用了模型…

[VSCode] VSCode 常用快捷键

文章目录 VSCode 源代码编辑器VSCode 常用快捷键分类汇总01 编辑02 导航03 调试04 其他05 重构06 测试07 扩展08 选择09 搜索10 书签11 多光标12 代码片段13 其他 VSCode 源代码编辑器 官网&#xff1a;https://code.visualstudio.com/ 下载地址&#xff1a;https://code.visua…

在学习爬虫前的准备

1. 写一个爬虫程序需要分几步 获取网页内容。 我们会通过代码给一个网站服务器发送请求&#xff0c;它会返回给我们网页上的内容。 在我们平时使用浏览器访问服务器内容是&#xff0c;本质上也是向服务器发送一个请求&#xff0c;然后服务器返回网页上的内容。只不过浏览器还会…

Oracle VM VirtualBox xx needs the Micrsoft Visual C++ 2019错误

错误展示 解决方法 重修安装 Visual C 文件 1、前往官网 C 中 Windows 编程概述 | Microsoft Learn 2、找到对应的包 左边导航栏依次选择&#xff1a; 部署本机桌面应用程序-----重新分发Visual C 文件-----最新受支持的Visual C可再发型程序包下载 根据自己电脑系统进行选…

数据结构期末复习笔记

文章目录 数据结构期末复习第一章&#xff1a;数据结构绪论第二章&#xff1a;顺序表与单链表第三章&#xff1a;其它链表第四章&#xff1a;栈如何中缀转后缀后缀如何计算 第五章&#xff1a;队列第六章&#xff1a;串第七章&#xff1a;树的概念和遍历第八章&#xff1a;赫夫…

创建一个郭德纲相声GPTs

前言 在这篇文章中&#xff0c;我将分享如何利用ChatGPT 4.0辅助论文写作的技巧&#xff0c;并根据网上的资料和最新的研究补充更多好用的咒语技巧。 GPT4的官方售价是每月20美元&#xff0c;很多人并不是天天用GPT&#xff0c;只是偶尔用一下。 如果调用官方的GPT4接口&…

Win10 自带微软输入法怎么切换成简体字 快捷鍵是什么?

环境&#xff1a; Win10专业版 问题描述&#xff1a; 微軟輸入法怎麽切換中文簡體 快捷鍵&#xff0c;之前不小心按了快捷键 解决方案&#xff1a; 1.按CtrlShiftF快捷键转换简体字或繁体字 2.可以在“设置-时间和语言-区域和语言-语言-中文&#xff08;中华人民共和国&a…

为什么选择嬴图?

图数据库、图计算、图中台都是用图论的方式去构造实体间的关联关系&#xff0c;实体用顶点来表达&#xff0c;而实体间的关系用边来表达。图数据库的这种简洁、自由、高维但100%还原世界的数据建模的方式让实体间的关联关系的计算比SQL类的数据库高效成千上万倍。 图&#xff1…

游戏版 ChatGPT,要用 AI 角色完善生成工具实现 NPC 自由

微软与 AI 初创公司 Inworld 合作&#xff0c;推出基于 AI 的角色引擎和 Copilot 助理&#xff0c;旨在提升游戏中 NPC 的交互力和生命力&#xff0c;提升游戏体验。Inworld 致力于打造拥有灵魂的 NPC&#xff0c;通过生成式 AI 驱动 NPC 行为&#xff0c;使其动态响应玩家操作…

计算机基础面试题 |19.精选计算机基础面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【复习】人工智能 第7章 专家系统与机器学习

专家系统就是让机器人当某个领域的专家&#xff0c;但这章专家系统不咋考&#xff0c;主要靠书上没有的机器学习。 一、专家系统的基本组成 二、专家系统与传统程序的比较 &#xff08;1&#xff09;编程思想&#xff1a; 传统程序 数据结构 算法 专家系统 知识 推理 &…

模型\视图一般步骤:为什么经常要用“选择模型”QItemSelectionModel?

一、“使用视图”一般的步骤&#xff1a; //1.创建 模型(这里是数据模型&#xff01;) tabModelnew QSqlTableModel(this,DB);//数据表 //2.设置 视图的模型(这里是数据模型&#xff01;) ui->tableView->setModel(tabModel); 模型种类&#xff1a; QStringListModel…

golang 记录一次协程和协程池的使用,利用ants协程池来处理定时器导致服务全部阻塞

前言 在实习的项目中有一个地方遇到了需要协程池的地方&#xff0c;在mt推荐下使用了ants库。因此在此篇记录一下自己学习使用此库的情况。 场景描述 此服务大致是一个kafka消息接收、发送相关。接收消息&#xff0c;根据参数设置定时器进行重发。 通过这里新建kafka服务&a…

【Spring实战】27 统一异常处理最佳实践

文章目录 1. 自定义异常2. 统一异常处理3. 配置4. 应用5. 启动类6. 启动服务7. 验证8. 优点总结 在 Spring 项目中&#xff0c;有效的异常处理是确保应用程序稳定性和用户体验的关键因素之一。通过实现统一异常处理&#xff0c;我们能够更好的管理和响应应用程序中的各种异常情…

vue elementUI Tree 树形控件的使用方法

用清晰的层级结构展示信息&#xff0c;可展开或折叠。 效果演示 trees.vue代码 <template><div><!-- 树形控件 --><el-tree :data"treesList" :props"treesProps" show-checkbox node-key"id"default-expand-all :defau…

关于TLS相关安全配置问题

近期被信安部门反馈了TLS几个安全漏洞。 SSL/TLS协议信息泄露漏洞(CVE-2016-2183)【原理扫描】SSL/TLS 受诫礼(BAR-MITZVAH)攻击漏洞(CVE-2015-2808)【原理扫描】SSL/TLS 服务器瞬时 Diffie-Hellman 公共密钥过弱【原理扫描】SSL/TLS RC4 信息泄露漏洞(CVE-2013-2566)【原理扫…