C语言核心技术-C语言概述与开发环境搭建
1.1 信息技术发展趋势
目前信息技术主要经历了互联网、移动互联网以及以大数据、云计算、物联网、人工智能、区块链等为代表的新兴技术三个阶段。
-
互联网
互联网自从20世纪90年代逐渐兴起,主要是通过网络连接了世界各地的PC,笔记本以及背后提供各种服务的大型服务器集群。
其中绝大多数PC、笔记本都运行着Windows,macOS操作系统,而服务器主要是以类Unix(CentOS,Ubuntu)占据主要市场。
依靠互联网成长起来的公司有Apple、Google、Amazon、Microsoft、Baidu、Alibaba、Tencent。 -
移动互联网
移动互联网主要从2010开始爆发式增长,主要是通过网络连接了世界各地的移动设备(最典型的就是手机),它们绝大多数都运行着Android,iOS操作系统。
依靠移动互联网成长起来的公司有字节跳动、蚂蚁金服、小米、美团、滴滴。 -
大数据、云计算
随着用户的爆发式增长,以海量数据为基础的大数据、云计算技术在Google、Amazon、Apple、Baidu、Alibaba、Tencent等大型互联网公司有着广泛的商业应用场景。 -
物联网
物联网会以手机作为中枢,通过物联网连接所有的智能设备,包括智能家居、汽车、电视等嵌入式设备,目前国内的小米、华为等在智能家居、电视、汽车等领域广泛布局。而随着5G的逐渐普及,万物互联的时代即将到来。 -
人工智能
人工智能(AI)作为当前最火爆的技术,国内的Baidu、Tencent、Alibaba等互联网公司逐渐根据自身的核心业务开始布局,Alibaba最早将人工智能技术布局在电商和物流领域,而Baidu开发出了对话式人工智能操作系统Dueros和自动驾驶平台Apollo,Tencent则是在游戏领域应用人工智能。 -
区块链
区块链经历了数字货币、智能合约和超级账本三个阶段,区块链技术可以广泛使用在金融、供应链、物流、公共服务等领域,解决互联网的信任问题,目前蚂蚁金服、京东已经有大量的区块链应用落地。
1.2 浅谈计算机系统
1.2.1 计算机系统组成
计算机是一种用于高速计算的电子机器,可以进行数值运算、逻辑判断,还有存储记忆功能,能够接收和存储信息,并按照存储在其内部的程序对海量数据进行自动、高速的处理,然后把处理的结果输出的现代化智能电子设备。
日常生活中常见的台式机、笔记本、手机等等都属于计算机。
计算机系统由硬件(Hardware)系统和软件(Software)系统两大部分组成。
软件是运行在硬件之上,而硬件的好坏会直接影响软件运行的性能。
计算机通过软硬件的共同协作就可以完成日常常见的任务,例如听歌、看电影、购物、聊天等等。
1.2.2 计算机硬件系统
计算机硬件系统主要由主机和外部设备组成。
主机主要是包含中央处理器和内存储器。
中央处理器主要由运算器和控制器组成,实现数据的数值计算、逻辑判断和其他设备控制。
而内存储器分为RAM和ROM,其中RAM(Random Access Memory)表示随机存储内存,程序运行时指令和数据都会被加载到内存中,这种存储器在断电时将会丢失其存储的内容。而ROM(Read-Only Memory)表示只读内存,是一种只能读出事先存储数据的固态半导体存储器。
外部设备主要包含外部存储器、输入设备、输出设备组成。常见的外存储器包含移动硬盘U盘等等。外部存储器通常用于大容量存储数据,IO性能比内存要低的多,但是只要硬件设备不坏的情况下能够永久存储数据,而常见的输入设备包含鼠标键盘等等,常见的输出设备包含声卡、显卡等等。网卡也是属于输入输出设备的一种,主要用于网络上的数据接收和发送。
在后期程序排查错误时除了要考虑程序本身的错误以外,还要考虑计算机硬件故障,例如内存不足,磁盘写满,网络不通等问题
每个计算机组件的IO性能也各不相同,CPU、内存、磁盘都遵循容量越大、IO性能越差的特点。
在后期优化程序时需要考虑程序时间复杂度和空间复杂度的问题。
1.2.3 计算机软件系统
软件的出现实现了人和计算机更好的交互,软件是由开发人员采用某种编程语言(C/C++/Java等等)编写的一系列指令以及不同业务场景产生的数据组成。
- 软件的分类
通常将软件分为系统软件和应用软件
驱动程序、操作系统、语言处理程序等都是属于系统软件。
驱动程序是硬件厂商使用编程语言(C/C++)编写的程序,在使用硬件(例如打印机、显卡、声卡等等)前必须安装对应的驱动程序。
操作系统是负责通过驱动程序管理硬件资源,同时给应用软件提供运行平台,目前主流的操作系统有Windows,Linux,macOS,Android,iOS,鸿蒙。
语言处理程序是用于将编程语言编写的源程序编译或者解释成对应平台的机器语言的程序。
微信、支付宝、淘宝、WPS等等都是属于应用软件,它们都是运行在操作系统之上的。
- 软件的交互方式
运行在不同操作系统之上的软件交互方式也不一样,Linux上的软件通常是使用字符界面的交互方式,运行在Windows,macOS上的软件通常是以鼠标、键盘操作的交互方式,运行在Android,iOS上的软件通常是以手势触控、语音(例如苹果的Siri,三星的Bixby)等交互方式。C语言阶段开发的程序绝大多数都是使用字符界面交互方式实现。
1.3 程序和指令
程序是为了完成某项任务(例如聊天、听音乐)而使用某种编程语言(例如C/C++)编写的一组指令序列。
而指令是计算机程序控制的最小单位,由操作码和操作数组成(例如1+2,其中1和2是操作数,+是操作码),操作码是计算机要执行什么操作,例如加减乘除操作,而操作数具体执行的对象,具体的数据以及数据的位置。
所有指令的集合称为指令系统,常见的PC机指令系统有Intel X86指令集,常见的手机指令系统有ARM,因此手机上的程序不加修改是不能直接在PC机上运行,因为不同的指令集是不兼容的。
那么计算机是如何处理程序的?
根据冯诺依曼计算机结构的原理,计算机的工作流程大致如下
当用户从计算机中打开程序时(鼠标点击桌面的快捷方式图标),程序开始执行。
操作系统将程序的指令和相关的数据传送到内存中。
CPU根据程序内容从内存中读取指令。
CPU分析、处理指令,并为下一条指令做准备,如果有涉及到调用硬件资源(例如调用声卡播放音乐)的指令,操作系统会通过调用对应的硬件驱动程序来实现调用硬件的功能。
CPU取下一条指令并分析、处理,如此重复操作,直到执行完程序中的全部指令,最后将计算的结果放到指令指定的存储器地址中。
1.4 浅谈计算机编程语言
1.4.1 计算机编程语言发展史
计算机已经成为人类大脑的延伸,成为不可或缺的一部分。而算法是计算机的灵魂,编程语言是塑造计算机灵魂的工具。让计算机具备"灵魂",可以按照人的意志运行,甚至某天按照计算机自己的意志运行,其核心就是算法。
计算机程序设计语言经历了机器语言到汇编语言和高级程序设计语言三个阶段,其特点是使得程序员用编程语言开发、测试、部署应用程序越来越方便、高效。但是是以牺牲效率为代价,但是随着计算机硬件的快速发展,绝大多数应用场景的效率损失可以忽略不计。
-
机器语言
计算机发展的最早期,程序员编写程序采用二进制的指令(010010101)来实现的,而每种CPU都有各自不同的指令系统(SPARC/Intel X86/ARM),因此在不同的机器上使用不同的机器语言实现。其特点是性能特别高效,而面向机器编程也就意味着不能移植,需要手动处理底层硬件的差异性,而且二进制的指令难以理解和维护。 -
汇编语言
随着时代和计算机技术的发展,汇编语言和编译器的出现解决了机器语言需要记住非常多的二进制机器指令的难题,但是还是没有从根本上解决移植性的问题,只是将机器指令转换为易懂的英文单词,然后由编译器编译成机器指令,因为计算机终归揭底只能识别二进制的0001110100110机器指令,而且汇编语言是面向机器的,不同机器(SPARC/Intel X86/ARM)的汇编指令是不相同的。尽管如此汇编语言目前仍然在工业电子编程、软件的加密解密和计算机病毒分析等场景广泛使用。 -
高级程序设计语言
高级程序设计语言的高级之处体现在开发人员在编写程序时无需关心计算机底层硬件差异而只需要专注于业务模块实现即可。而且相对于机器语言、汇编语言而言,高级语言学习起来更加容易,因此会有更加充足的人力资源,也更容易开发大型商业软件,例如Windows、macOS、Linux,Android,IOS等操作系统都是使用C/C++实现。国内的阿里巴巴在电商、物流、金融、支付领域大量使用Java实现。
1.4.2 计算机语言应用场景
如果想知道目前主流的编程语言有哪些,可以访问tiobe 首页获取编程语言排行榜。
2019年12月编程语言排行榜Top20
根据Tiobe排行榜得知,Java/C/Python分别排在前三名, 随后紧跟着C++,C#,JavaScript等等。
每种编程语言都有自己擅长的应用场景。
编程语言 | 应用场景 |
---|---|
C | 硬件驱动、操作系统、系统软件 |
C++ | 系统软件、网络通讯、科学计算 、游戏 |
C# | Windows应用,Web应用、游戏 |
Java | 大型互联网应用(淘宝、天猫),Android,大数据 |
Go | 高并发、区块链、微服务 |
Kotlin | Android应用开发 |
Swift | macOS,iPhone,iPad应用开发 |
Python | 人工智能、机器学习、自动化运维、数据分析 、图形处理 |
JavaScript | 浏览器端、服务端、PC桌面 |
Scala | 大数据开发 |
那么如果你作为初学者,面对如此之多的编程语言,到底应该先从哪门语言上车呢?如果你想深入的学习其他语言和架构相关的知识之前建议熟练掌握C语言。因为C++/Java/C#等编程语言都模仿了C语言。无论哪种语言,都是实现目标的工具,而不是目标本身。
而不出意外的话,我的Devops 头条号会覆盖上述的十种编程语言,目的是实现全平台(Windows,Linux,macOS)应用开发、测试、部署。感兴趣的同学可以关注。
1.5 程序的编译和解释
由于高级编程语言和计算机底层语言是互不相通的,计算机最底层只能识别01010这种二进制的机器指令,因此需要使用工具进行语言之间的转换。这个就是程序编译或者解释的由来。
为了理解程序编译和程序解释,这里引入两种语言,分别是高级计算机语言L1,以及低级计算机语言L0。
-
程序编译
开发人员使用高级编程语言L1编写的应用程序通过编译器生成成计算机底层能够识别的低级语言L0,L0就是计算机实际执行的语言,而编译器生成的计算机底层识别的二进制机器指令过程就是程序编译。常见的编译型语言有C/C++/Objective-C/Go -
程序解释
首先使用L0语言实现另外一个程序,也就是解释器,然后将开发人员使用高级语言L1编写的应用程序作为输入,每一句L1编写的程序都会在L0中作为等价的转换然后执行,解释的过程就是由L0编写的解释器去解释L1程序。常见的解释型语言有PHP,Python,JavaScript。
而Java和C#属于编译+解释型语言,以Java为例子,Java程序在运行之前首先被编译成Java字节码,然后在JVM虚拟机上解释运行。
1.6 C语言概述
1.6.1 C语言发展简史
- 起源
1972年,贝尔实验室的Dennis Ritchie和Ken Thompson在开发Unix操作系统时设计了C语言,该操作系统的90%以上的代码都是由C语言实现,后期的Linux,macOS,Android,IOS都是基于Unix发展而来的。 - 标准
1987年Dennis Ritchie和Brian Kernighan编写了The C Programming Language第一版是公认的C标准实现,而没有定义C库。
而后期ANSI/ISO先后于1990年、1999年和2011年发布了C90标准、C99标准和C11标准,该标准定义了C语言和C标准库。
1.6.2 C语言特性
C语言作为面向过程的高级程序设计语言,能够轻松的实现自顶向下的规划、结构化编程和模块化设计,这样使得程序结构更加简洁,可扩展性强以及更加容易维护。
C语言有着高效(执行速度快,仅次于汇编)、功能强大(嵌套汇编)以及可移植性(标准库可移植)、功能强大等优点。
而且也存在着对系统平台库依赖严重,由于编程风格自由,经验不足也会容易导致出错,编写代码实现周期长,同样的代码在不同的操作系统(或者编译器)下可能会有不同的表现等缺点。
1.6.3 C语言应用场景
C语言偏向硬件驱动、操作系统、底层应用(数据库、编译器、解释器)、嵌入式应用开发、游戏引擎、物联网等应用场景。
硬件驱动的绝大部分实现是由C语言和汇编语言实现的。
主流操作系统(Unix,Linux,macOS,Windows,Android,iOS)的底层实现都是由C语言和部分汇编实现的。
C++,Java,Python,Swift的编译器或者解释器都是由C语言实现的。
Git,Nginx,Redis,MySQL,Apache,Linux Kernel都是使用C语言实现的,而且都是开放源代码的,大部分代码可以在开源项目托管平台GitHub中获取到,可以通过阅读源码提升自己的设计和编码能力。
Redis
Nginx
1.7 使用Visual Studio Code编写C程序
1.7.1 Visual Studio Code下载与安装
Visual Studio Code是微软推出的一款开源、跨平台(Windows,Ubuntu,macOS)的编辑器,这里使用它来编写C语言的源代码,当然除了编写C代码之外,还可以用来编写C++,Java,Python,Go,JavaScript,PHP程序等等。
为什么首先使用Visual Studio Code来编写C程序而不是功能强大的Visual Studio 2019来实现呢
因为作为一位初学者来讲,应该要理解程序运行的背后机制,而Visual Studio 2019封装了大量的细节,不便于初学者理解,在后期进行项目开发时可以选用Visual Studio 2019作为C语言的开发工具。
在使用Visual Studio Code之前首先去官网下载
然后傻瓜一键式安装,安装完成以后启动Visual Studio Code会看到启动界面。
关于Visual Studio Code的详细使用可以参考官方提供的文档。
1.7.2 使用Visual Studio Code编辑源文件
因为windows10默认不显示文件后缀名,这里直接在Visual Studio Code中编辑源文件可以避免该问题,同时也可以在文件资源管理器中勾选文件扩展名,这样当我们在文件系统中查看文件时可以看到文件的后缀名。
1.在系统的D盘下创建目录vs_codec用于存放C语言的源程序(即C代码)
2.在Visual Studio Code中将D:vs_codec目录添加到工作区的文件夹
选择D:vs_codec文件夹作为Visual Studio开发C程序的工作区
3.在工作区D:vs_codec目录下创建源文件helloworld.c,C语言的源文件都是以.c结尾的,并编写以下内容
#include <stdio.h>
int main(int argc, char *argv[]){
printf("Windows10 1903 & Visual Studio Code Hello World With C
");
return 0;
}
1.8 使用GCC编译器编译C程序
1.8.1 GCC安装与配置
当源文件编辑好完成以后,需要安装和配置gcc来编译C程序,因为Visual Studio Code并没有提供编译C程序的功能。
如果你之前已经下载安装过Qt,那么恭喜不用额外安装gcc,因为Qt已经集成了编译器。
只需要将gcc在Qt的 C:QtQt5.13.0Toolsmingw730_64in
目录: 添加至PATH环境变量即可。
当在终端上执行gcc命令时系统会从该目录寻找gcc对应的可执行程序gcc.exe。
当gcc的PATH环境变量配置成功之后,便可以打开命令提示符(即终端),输入gcc -v便可查看gcc版本信息
1.8.2 GCC编译源文件
在源文件helloworld.c所在的目录地址栏(例如D:vs_codec)输入cmd并回车即可打开终端,此时终端的路径正好是在D:vs_codec,然后使用gcc helloworld.c
编译源文件,Windows上gcc默认会编译生成可执行文件a.exe,Linux下gcc默认会编译生成a.out。
然后在终端上运行a.exe,程序会将字符串Windows10 1903 & Visual Studio Code Hello World With C输出到终端上。
如果想要改变编译生成的可执行文件名称,使用gcc时可以使用-o选项实现
gcc helloworld.c -o helloworld.exe
此时使用GCC编译程序时会生成helloworld.exe
1.8.3 理解GCC的编译过程
刚才在使用gcc编译helloworld时一次性编译链接生成了可执行文件(例如helloworld.exe),而实际上gcc在编译程序时要经历预处理、编译、汇编、链接四个步骤,每个步骤gcc都提供了对应的选项(链接除外),并且每个步骤生成的文件后缀名也是不一样的。
这里编写源文件gcc_compile_option.c,用于测试gcc的各个编译选项,如果想要查看gcc命令的所有选项,可以使用gcc --help
查看所有选项及其说明
#include <stdio.h>
#define PI 3.141592 //宏定义
int main(int argc, char *argv[]){
printf("PI = %f
",PI);
return 0;
}
- 预处理
预处理主要是头文件包含、宏替换以及条件编译,预处理阶段不会做语法检查。
预处理生成的文件后缀名通常是.i,而对应的gcc选项是-E。
gcc预处理命令案例
gcc -E gcc_compile_option.c -o gcc_compile_option.i
这里预编译会将头文件stdio.h的内容复制到gcc_compile_option.i头文件中,并且将使用#define PI 3.141592的宏进行替换,因此最后运行程序时会看到终端上输出PI =3.141592
- 编译
编译是将预处理完成的文件编译成汇编文件。
编译生成的文件后缀名通常是.s,而对应的gcc选项是-S。
gcc编译案例
gcc -S gcc_compile_option.i -o gcc_compile_option.s
- 汇编
汇编是将汇编文件生成对应的二进制文件。
汇编生成的文件后缀名通常是.o,而对应的gcc选项是-c。
gcc汇编案例
gcc -c gcc_compile_option.c -o gcc_compile_option.o
- 链接
将各个二进制文件加上库函数、启动代码链接生成可执行文件。
链接没有特定的gcc选项,windows上的可执行文件名为.exe,linux上可执行文件后缀名为.out。
gcc 链接案例
gcc gcc_compile_option.o -o gcc_compile_option.exe
在日常开发时通常是使用GCC直接将C程序编译成可执行文件后运行。
1.9 C语言集成开发环境搭建
目前主流的操作系统(Windows,Linux,macOS)都有完善的C语言集成开发环境,用于编辑、编译、调试、打包部署C程序。
操作系统 | 开发工具 |
---|---|
Windows10 1903 | Visual Studio 2019 |
macOS10.15 | XCode10.3 |
Ubuntu18.04 | QT5.13 |
Windows作为世界上最流行的桌面操作系统,当前最新版本为Windows10 1903,VisualStudio作为Windows上最强大的集成开发环境,可以开发Windows软件,游戏,Web应用等等,最新版本为VisualStudio2019 16.4.1。
Ubuntu作为最受欢迎的桌面版Linux系统之一,推荐采用跨平台的集成开发环境Qt来编写C/C++程序。
QT Creator
macOS平台推荐使用XCode来编写C/C++程序,而且XCode在macOS系统上是自带的,无需额外安装。
XCode
除此以外还有些跨平台的C/C++ 开发工具,例如来自Jetbrains公司的CLion也可以用来编写C/C++程序。
CLion
关于IDE的安装以及使用,它们的官网都提供了详细的教程,同学们可以自行去查阅。
个人在C语言学习过程中会使用Windows10 1903+Visual Studio 2019作为主力开发环境,如果没有特别的说明,默认的C语言开发环境就是Windows10 1903和Visual Studio 2019。
1.10 Visual Studio 2019集成GitHub
在Visual Studio 中使用GitHub插件的前提是需要去Git官网下载和安装Git客户端。
在使用Visual Studio 2019开发C语言的程序前,可以集成GitHub插件,便于后期代码统一管理。
Visual Studio 集成GitHub之前首先需要从Visual Studio 提供的插件市场中搜索并下载GitHub插件。
下载完成后直接点击GitHub.VisualStudio-v2.10.8.8132.vsix安装即可,安装前需要关闭Visual Studio 2019,否则无法正确安装。
Initializing
Install
正在安装GitHub Extension for Visual Studio
安装完成
安装完成之后启动Visual Studio
并切换到团队资源管理器视图
然后点击GitHub的连接,并输入自己的GitHub账号登录
登录成功之后,点击GitHub下方的创建按钮来创建GitHub仓库用于存储c-core项目,
创建项目时需要填写项目的名称、描述以及项目本地的路径、Git忽略、许可证信息。
填写完毕后点击创建即可。
然后访问GitHub时可以看到远程已经创建完成c-core仓库,后续所有的C程序代码都会存放在该仓库中。
1.11 使用Visual Studio 2019开发C程序
在使用Visual Studio 2019开发C程序时首先需要创建项目,Visual Studio 2019是以项目为单位来管理程序,每个解决方案下可以创建多个项目,项目中可以创建筛选器,其作用类似于文件夹。然后在筛选器中存放多个C语言的源文件(后缀名为.c)和头文件(后缀名为.h),但是解决方案下的每个项目中只能有一个main方法,否则程序会运行出错。
如果想要使用GitHub来管理项目,我们还可以在团队资源管理器视图中将项目推送到GitHub。
-
创建新项目
切换到团队资源管理器视图,在之前创建的Git仓库c-core基础上新建项目
-
选择基于C++的空项目
-
配置新项目
设置项目名称为c-core-foundational,
项目路径位于d:workspacecvisualstudio2019,
解决方案名称为c-core。
Visual Studio 2019中一个解决方案会有多个项目。
项目概览
4. 创建筛选器
筛选器用于存放不同阶段的代码,在项目的源文件下创建筛选器net/ittimeline/c/core/foundational/object
- 编写helloworld
首先选中筛选器net/ittimline/c/core/foundational/object
然后选择基于C++文件,创建源文件helloworld.c
然后编辑源文件helloworld.c
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Windows10 1903 & Visual Studio 2019 Hello World With C
");
return 0;
}
-
gcc编译运行helloworld.c
首先选择项目c-core-foundational,然后鼠标右键选中在文件资源管理器打开。
此时会打开文件资源管理器,并且路径是D:workspacecvisualstuido2019c-corec-core-foundational
在文件资源管理器的地址栏输入cmd,然后使用gcc编译、运行程序
-
将代码上传至GitHub
首先切换到团队资源管理器视图,然后点击更改
然后编写提交说明,并选择提交的文件后点击全部提交
提交后点击同步和推送即可推送到远程的GitHub上。
同步
推送
推送成功
推送成功之后就可以在GitHub中查看推送的文件
1.12 C语言程序的基本结构
helloworld程序
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
printf("Windows10 1903 & Visual Studio 2019 Hello World With C
");
system("pause");
return 0;
}
此段程序是调用了跨平台的输出函数printf()
实现在控制台输出Windows10 1903 & Visual Studio 2019 Hello World With C 并换行,然后等待用户输入任意键(通常习惯按回车键)后程序退出。
虽然该程序十分简单,但是是C语言中能够运行的最小程序。后面编写的程序都是在该基础之上扩展的。
C语言中以#开头的表示编译时预处理阶段需要处理的。
因为在main函数中使用了printf函数,但是该函数的声明是在标准输入输出文件stdio.h头文件中。因此需要使用#include <stdio.h>
将其包含在源文件helloworld.c中,该文件位于系统目录 C:Program Files (x86)Windows Kits10Include10.0.18362.0ucrtstdio.h,而该函数的实现在系统库文件中。
<>
表示在系统目录查找,而""
表示在当前目录下查找。
如果想要查看程序依赖了哪些库文件,Ubuntu下可以使用ldd命令查看
std表示跨平台的,所有以std开头的头文件中声明的函数都是可以跨平台(windows,linux,macOS)使用。
stdio表示标准输入输出,printf函数就是用于输出,可以将""包含的内容数据输出到控制台,文件甚至是网页。而输入函数有scanf,用于读取键盘的输入,实现程序的交互。该函数后期再介绍详细使用。
如果想要在Visual Studio 2019中查看系统提供的函数的定义,可以选中函数,然后按快捷键F12便可查看,而返回可以使用Ctrl+-实现。
为了深入理解#include的作用,这里编写一段程序。
首先在c-core-foundational的头文件创建和源文件一样目录的筛选器netittimelineccorefoundationalobject
然后定义头文件include.h
然后将之前编写的源文件helloworld.c的内容复制到include.h头文件中
并在源文件的netittimelineccorefoundationalobject目录下创建源文件include.c,该源文件中只需要使用#include "include.h"
包含之前编写的头文件即可。
然后运行程序,如果发现如下错误
1>include.obj : error LNK2005: _main 已经在 helloworld.obj 中定义
是因为在Visual Studio 中,解决方案的一个项目(例如c-core-foundational)只能有一个main函数,如果包含了多个源文件,把其他源文件的main函数重命名成其他函数即可。
然后再次运行程序,程序结果和之前的helloworld运行结果是一样的。
main函数是c/c++/java程序的入口函数,有且只能有一个,当程序执行时系统会自动调用唯一的main函数,程序从main函数开始执行。如果程序中编写了其他的函数,其他的函数应该被main函数调用才会得到执行。
例如定义一个返回两个整数相加的函数add,然后想要执行该函数,则必须在main函数调用。
#include <stdio.h>
#include <stdlib.h>
//自定义函数,返回两个整数相加的结果
int add(int left,int right) {
return left + right;
}
int main(int argc, char* argv[])
{
int left = 10;
int right = 20;
//调用add函数,并定义变量保存结果
int result =add(left,right);
printf("%d + %d = %d",left,right,result);
system("pause");
return 0;
}
main函数左边的int表示函数的返回类型为整数,如果不想返回可以声明为void。
main函数右边的()表示函数的参数,通过传递参数,将外部数据传入函数内部处理。
{}表示函数的函数体,函数具体做什么事情,都写在{}内,{}是成对出现的。
main函数中的 printf("Windows10 1903 & Visual Studio 2019 Hello World With C
");
表示将Windows10 1903 & Visual Studio 2019 Hello World With C输出到控制台,
表示换行。而;表示语句结束,C语言中的每条语句都要使用;表示结束,否则程序会编译错误。
main函数中的system("pause")
是让程序暂停,等待用户回车后退出程序。
return 0 表示返回给系统0,并结束main函数,0表示main函数正常退出。
return之后的代码都不会执行。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
printf("Windows10 1903 & Visual Studio 2019 Hello World With C
");
system("pause");
return 0;
printf("Windows10 1903 & Visual Studio 2019 Hello World With C again
");
}
运行上面的程序,Windows10 1903 & Visual Studio 2019 Hello World With C again
不会得到输出。
注释是用于描述程序的功能,为了便于程序后期的维护,在开发过程中应该添加适量的注释,编译器在预处理时会删除注释,所以注释的内容不会影响程序的结果。
C语言中支持的注释包含单行注释(//
)和多行注释(/**/
)两种,单行注释顾名思义就是注释一行,多行注释可以注释多行,需要注意的是多行注释不能够嵌套使用,否则会发生编译错误,因为编译器是从上到下编译。
增加单行注释和多行注释后的helloworld程序
#include <stdio.h>
#include <stdlib.h>
/*
main函数左边的int是函数的返回值,右边()的内容表示函数的参数,外部数据通过函数的参数传递到函数的内部处理。
{}表示函数的函数体,也就是函数执行的所有内容都包含在{}中,它是成对出现的。
@author liuguanglei ittimelinedotnet@gmail.com
@wechat 18601767221
@website ittimeline.net
@version 2019/12/13
*/
int main(int argc, char* argv[]) {
// printf函数是系统自带的函数,用于将""包含的字符串按照指定的格式输出到终端,终端可以是控制台、网页或者是文件,目前是输出到控制台。
printf("Windows10 1903 & Visual Studio 2019 Hello World With C
");
//等待输入任意内容退出程序
system("pause");
return 0;
}
1.13 Windows命令
1.13.1 Windows命令概述
主流的操作系统(Windows,Linux(Ubuntu),macOS)及大多数应用程序都提供了基于命令行和图形化界面两种交互方式,而移动端是采用手势触控、语音等进行交互。作为普通用户来讲,图形化界面容易上手,而且交互效果更加直观。
但是作为一个程序员来讲,应该去熟悉各个操作系统的命令行的使用,因为命令行相对图形化界面而言,绝大多数场景下使用更加高效。而且图形化界面本质是对命令行的封装,能用图形化界面实现的基本都可以采用命令行实现。而且在服务器端(CentOS,RedHat)基本上是不会安装图形界面。
Windows下想要使用命令,必须首先启动控制台。
Windows系统可以使用快捷键Windows+r调出运行
后输入cmd,便可以进入Windows下的命令行终端
1.13.2 日常开发中Windows常用命令
在启动Windows的终端(也称为控制台)之后,便可以运行命令了。如果你使用Windows作为开发平台,如下的命令你也许会经常使用到。
命令名称 | 功能描述 |
---|---|
dir | 列出当前目录列表 |
cd | 切换目录(只能在根盘符(例如C盘)内切换),cd /切换到磁盘的根目录 |
md | 创建目录 |
rd | 删除空目录 |
del | 删除文件 |
type | 显示文本文件内容 |
echo | 输出文本内容 例如echo %path% 查看系统环境变量 |
ping | 检测网络,例如ping baidu.com |
notepad | 记事本 |
calc | 计算器 |
mspaint | 画图 |
explore | 文件资源管理器 |
cleanmgr | 磁盘清理 |
desk.cpl | 分辨率设置 |
powercfg.cpl | 电源设置 |
regedit | 注册表编辑器 |
msconfig | 系统配置 |
mstsc | 远程连接 |
firewall.cpl | 防火墙 |
appwiz.cpl | 添加或修改程序 |
tasklist | 查看进程列表 |
taskkill /f /im process-name.exe | 关闭指定进程 |
netstat -ano|findstr "port" |
查看端口占用 |
msinfo | 系统信息 |
sticky notes | 便签 |
ipconfig | 查看ip |
winver | 查看windows版本 |
cls | 清除屏幕内容 |
pause | 暂停 |
exit | 退出终端 |
在终端上使用命令时还有些快捷键,例如Tab可以补全路径,上下键可以切换之前输入的命令。*
实现模糊匹配操作,例如 del *.txt
就可以删除 指定目录的所有文本文件。
如果想要获取更多的windows命令信息,可以在终端上输入help回车后会显示每个命令的名称以及功能说明。
1.14 system函数的使用
1.14.1 system函数调用系统命令
system函数用于C程序中调用各大操作系统的命令和应用程序,被调用的命令和应用程序以字符串的方式作为system函数的参数传入,实现读取用户输入的命令,并执行该命令。
在windows上运行该程序时可以传入notepad,calc等等实现程序调用。
而Linux和macOS上也有自己的命令可以提供给system函数来调用。
这里编写一个交互式程序:通过借助C语言的scanf函数读取用户在终端的命令,然后调用system函数来执行对应的命令。
如果你的开发环境不是Windows10 1903和Visual Studio 2019,可以删除#define _CRT_SECURE_NO_WARNINGS
。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
使用system函数调用各大操作系统的命令
@author liuguanglei ittimelinedotnet@gmail.com
@website ittimeline.net
@version 2019/10/17
*/
int main(int argc, char* argv[])
{
// 提示用户输入执行的命令
printf("请输入需要执行的命令
");
//定义字符串保存用户输入命令
char command[100] = {0};
//读取用户从键盘输入的命令字符串,并保存到command中
scanf("%s",command);
//执行命令
system(command);
system("pause");
return 0;
}
因为system函数位于stdlib.h头文件中,意味着它是一个可以跨平台使用的函数。
例如在Ubuntu下使用gedit命令调用文本编辑器
但是各大操作系统的命令和应用程序是不相同的,例如在Windows上使用system函数传递字符串参数"notepad"时可以打开记事本,而移植到了类Unix上则无法调用记事本。
1.14.2 system函数阻止visual studio 2019闪退
在Visual Studio 2019中运行程序可以通过快捷键F5直接运行,也可以通过点击本地Windows调试器运行。
但是发现在运行helloworld后程序有闪退现象,即程序运行时打开了终端后立即退出,其原因是程序运行是printf函数默认输出数据到终端,因此Visual Studio 2019开启了终端,printf输出数据完成以后,程序遇到return语句并退出,因此终端也会随之退出,因为弹出终端和退终端的时间非常短,所有感觉有闪退现象。
为了解决这一问题,首先需要在源文件使用#include <stdlib.h>
包含标准库头文件,然后在main函数的最后一行加上system("pause")
,这样程序运行时不会立即退出,而是等到用户输入任意键(一般习惯按回车键)才会退出。
解决 helloworld在Visual Studio 2019运行的闪退问题
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
printf("Windows10 1903 & Visual Studio 2019 Hello World With C
");
system("pause");
return 0;
}
再次运行程序时,在程序的末尾会输出一句请按任意键继续...
1.4.3 system函数的同步和异步特性
默认情况下system函数是同步执行的,如果使用system函数调用多个系统命令,会发现下一个系统命令的执行必须等待上一个执行结束才会得到执行。
以windows上使用system函数同时打开记事本和计算器为例,只有在关闭记事本后才能打开计算器。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
system函数的同步特性
@author liuguanglei ittimelinedotnet@gmail.com
@wechat 18601767221
@website ittimeline.net
@version 2020/01/02
*/
int main(int argc, char* argv[])
{ // system函数默认是同步执行
//只有关闭记事本后才能打开计算器
//打开记事本
system("notepad");
//打开计算器
system("calc");
system("pause");
return 0;
}
如果想要同时打开记事本和计算器,可以在system函数加上start参数。
这样就能让system函数异步执行。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
理解system函数异步特性
@author liuguanglei ittimelinedotnet@gmail.com
@wechat 18601767221
@website ittimeline.net
@version 2020/01/02
*/
int main(int argc, char* argv[])
{
system("start notepad");
system("start calc");
system("pause");
return 0;
}
使用system函数实现异步打开记事本,然后等待5秒钟关闭
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
/*
使用system函数实现打开记事本并五秒钟关闭
system函数默认是同步执行
需要加上start参数让system异步执行
@author liuguanglei ittimelinedotnet@gmail.com
@wechat 18601767221
@website ittimeline.net
@version 2020/01/02
*/
int main(int argc, char* argv[])
{
//异步打开记事本 如果不加start,只有等到关闭记事本才会执行下面的程序(默认同步)
system("start notepad");
//暂停5秒
Sleep(5000);
//关闭notepad进程 如果这里的system函数不加start参数,不会关闭notepad进程
//因为system函数默认是同步执行的
//system("taskkill /f /im notepad.exe");
system("start taskkill /f /im notepad.exe");
system("pause");
return 0;
}
1.15 C语言编程流程
在编写C语言程序时,需要经过如下几个流程
- 需求分析:
需求分析的目的就是要梳理程序所要实现的具体功能 - 概要设计:
设计功能所需要的完整步骤,将步骤封装成多个独立的模块。 - 编码
编码实现各个模块,并且组合各个模块完成对应的功能。 - 编译和运行程序
使用所在的平台对应的IDE来编译和运行程序,如果程序出现编译错误,则是编写的程序不符合C语言规范,如果是程序逻辑错误,需要调整对应的业务逻辑。 - 测试和调试程序
程序编写完成后应该进行功能测试,如果测试的过程中发现不符合预期的结果。可以通过IDE调试程序来查找问题,修复问题。 - 维护程序:
需求总是在变更的,如果程序开发途中遇到需求变更,需要修改设计方案,并且重新实现对应的设计方案。
假设用户提出一个需求:编程实现打开QQ,并且根据用户输入的秒数后关闭QQ,还要显示倒计时的效果(基于控制台实现)
概要设计: 封装三个函数,分别实现打开QQ,倒计时和关闭QQ
编码实现
#define _CRT_SECURE_NO_WARNINGS //解决scanf函数的安全警告
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
/*
C语言编程流程
1.需求分析
2.概要设计
3.编码
4.编译、运行程序
5.测试、调试程序
6.维护程序
@author liuguanglei ittimelinedotnet@gmail.com
@wechat 18601767221
@website ittimeline.net
@version 2020/01/02
*/
/*
打开QQ
*/
void open_qq() {
//本地QQ启动程序的路径 会被当作转义字符,因此使用/表示路径
//而""表示字符串,因此使用"表示"
system(""C:/Program Files (x86)/Tencent/QQ/Bin/QQScLauncher.exe"");
}
/*
倒计时
即根据用户输入的时间来关闭QQ
*/
void count_down() {
int number = 0;
printf("请输入多少秒后关闭QQ
");
//读取用户输入的时间(秒)
scanf("%d", &number);
printf("QQ将在%d秒后关闭
", number);
int count = 0;
//倒计时
while (count < number) {
//每循环一次,中断一秒
Sleep(1000);
printf("倒计时%d秒
", (number - count));
count++;
}
}
/*
关闭QQ
*/
void close_qq() {
printf("准备关闭QQ
");
//使用系统命令杀掉QQ进程
system("taskkill /f /im QQ.exe");
}
int open_and_close_qq_main(int argc, char* argv[])
{
//打开QQ
open_qq();
//倒计时
count_down();
//关闭QQ
close_qq();
system("pause");
return 0;
}
编译和运行程序:因为该程序是依赖头文件Windows.h,因此只能在Windows上运行(C语言对于平台的依赖就体现在这里),直接使用Visual Studio 2019的运行功能运行该程序。
运行程序的途中如果该程序不符合预期,则需要根据错误(编译错误、逻辑错误)进行调试和修改程序
程序运行结果
当运行程序时,系统会启动控制台等待用户输入指定的秒数,然后启动QQ
之后会运行倒计时,然后在用户指定的时间内关闭QQ
使用shellExecuteA()函数打开和关闭记事本
#define _CRT_SECURE_NO_WARNINGS //解决scanf函数的安全警告
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
/*
C语言编程流程
1.需求分析
2.概要设计
3.编码
4.编译、运行程序
5.测试、调试程序
6.维护程序
@author liuguanglei ittimelinedotnet@gmail.com
@wechat 18601767221
@website ittimeline.net
@version 2020/01/02
*/
/*
打开记事本
*/
void open_notepad() {
//ShellExecuteA()函数通过传递不同的参数,可以改变打开应用程序(例如这里记事本)的行为
//其中最后一个参数0 表示隐藏打开
//最后一个参数1表示正常打开
//最后一个参数3表示最大化打开
//最后一个参数6表示最小化打开
ShellExecuteA(0, "open", "notepad", 0, 0, 1);
}
/*
倒计时
即根据用户输入的时间来关闭QQ
*/
void countDown_notepad() {
int number = 0;
printf("请输入多少秒后关闭记事本
");
//读取用户输入的时间(秒)
scanf("%d", &number);
printf("记事本将在%d秒后关闭
", number);
int count = 0;
//倒计时
while (count < number) {
//每循环一次,中断一秒
Sleep(1000);
printf("倒计时%d秒
", (number - count));
count++;
}
}
/*
关闭记事本
*/
void close_notepad() {
printf("准备关闭记事本
");
//使用系统命令杀掉QQ进程
system("taskkill /f /im notepad.exe");
}
int main(int argc, char* argv[])
{
//打开记事本
open_notepad();
//倒计时
countDown_notepad();
//关闭记事本
close_notepad();
system("pause");
return 0;
}
使用ShellExecuteA()函数打开和关闭QQ
#define _CRT_SECURE_NO_WARNINGS //解决scanf函数的安全警告
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
/*
C语言编程流程
1.需求分析
2.概要设计
3.编码
4.编译、运行程序
5.测试、调试程序
6.维护程序
@author liuguanglei ittimelinedotnet@gmail.com
@wechat 18601767221
@website ittimeline.net
@version 2020/01/02
*/
/*
打开QQ
*/
void open_QQ() {
//ShellExecuteA()函数通过传递不同的参数,可以改变打开应用程序(例如这里的QQ)的行为
//其中最后一个参数0 表示隐藏打开
//最后一个参数1表示正常打开
//最后一个参数3表示最大化打开
//最后一个参数6表示最小化打开
ShellExecuteA(0, "open", ""C:/Program Files (x86)/Tencent/QQ/Bin/QQScLauncher.exe"", 0, 0, 1);
}
/*
倒计时
即根据用户输入的时间来关闭QQ
*/
void count_down_qq() {
int number = 0;
printf("请输入多少秒后关闭QQ
");
//读取用户输入的时间(秒)
scanf("%d", &number);
printf("QQ将在%d秒后关闭
", number);
int count = 0;
//倒计时
while (count < number) {
//每循环一次,中断一秒
Sleep(1000);
printf("倒计时%d秒
", (number - count));
count++;
}
}
/*
关闭QQ
*/
void close_QQ() {
printf("准备关闭QQ
");
//使用系统命令杀掉QQ进程
system("taskkill /f /im qq.exe");
}
int main(int argc, char* argv[])
{
//打开qq
open_QQ();
//倒计时
count_down_qq();
//关闭QQ
close_QQ();
system("pause");
return 0;
}
1.16 Visual Studio 2019 常用技巧锦集
1.16.1 Visual Studio 2019常用快捷键
在日常使用Visual Studio 开发中会使用到如下常用的快捷键
快捷键名称 | 功能 |
---|---|
Ctrl+k,f | 自动格式化代码 |
Ctrl+k,c | 注释代码 |
Ctrl+k,u | 取消注释代码 |
Ctrl+F5 | 不调试运行 |
Ctrl+shift+b | 编译,不运行 |
F12 | 查看函数定义 |
ctrl+- | 退出函数定义 |
F5 | 调试运行 |
F11 | 逐语句(遇到函数跳到函数内部逐语句执行) |
F10 | 逐过程 (不会进入函数内部,但是函数会被调用) |
Shift+F11 | 跳出 |
1.16.2 Visual Studio 2019 调试程序
在后期编写程序时,经验不足的开发人员通常会遇到两种错误:编译错误和运行时错误,编译错误通常是编写的程序不满足编译器的语法规范,而运行时错误则是程序运行时发生的错误,想要排查程序运行时的错误,就需要使用IDE提供的调试功能。
在源文件debug.c中定义一个返回两个整数相乘的方法mul,然后在main函数中定义两个变量作为参数传入mul方法中,并调用两个整数相乘的方法,输出计算结果
#include <stdlib.h>
#include <stdio.h>
/*
定义两个整数相乘并返回
*/
int mul(int source, int target) {
//打印输出传入mul方法的两个整数参数的值
printf("source = %d
",source);
printf("target = %d
", target);
return source * target;
}
/*
Visual Studio 2019的调试
@author liuguanglei ittimelinedotnet@gmail.com
@website ittimeline.net
@version 2019/10/17
*/
int main(int argc, char* argv[])
{
//定义两个变量
int source = 12;
int target = 12;
//调用两个整数相乘的方法,并将计算的结果保存到整数变量result中
int result = mul(source, target);
printf("result = %d
",result);
system("pause");
return 0;
}
在调试程序前首先需要下断点,可以使用快捷键F9下断点或者取消断点
然后使用F5调试运行程序,程序会在之前下过断点的地方暂停(无论是逐语句还是逐过程),此时可以在Visual Studio提供的自动窗口中观察变量的值
如果想要一行一行调试代码,可以使用快捷键F11,当遇到函数时(例如这里的mul函数),会进入函数的内部一行一行执行,直到该函数结束为止。
如果调试时不想进入函数的内部,可以使用快捷键F10,当程序中调用了其他函数时不会进入其他函数内部,但是函数会被调用。
如果调试时想要从一个断点跳到另外一个断点,可以使用shift+F11实现。
1.16.3 Visual Studio 2019的C4996错误解决
C4996是Visual Studio 2019提供的错误编号
首先明确下C4996错误出现错误的原因:
Visual Studio 希望用户使用Visual Studio提供的非标准库函数,但是程序员希望使用标准库函数,因为标准库是可以移植的。
如果在Visual Studio中使用标准的库函数,就会引发C4996错误类似于这种提示:error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details
.这里的strcpy是标准的库函数,而在Visual Studio中使用这个函数引发了C4996错误。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
解决visual studio 2019 的C4996错误
@author liuguanglei ittimelinedotnet@gmail.com
@website ittimeline.net
@version 2019/10/17
*/
int main(int argc, char* argv[])
{
//定义一个字符串
char str[32] = "";
//将hi string 复制到str中
strcpy(str, "hi string");
printf("str = %s
",str);
system("pause");
return 0;
}
那么如何解决问题呢?
方案1:在源文件的第一行定义宏#define _CRT_SECURE_NO_WARNINGS
方案2:在源文件的第一行定义#pragma warning(disable:4996)
解决Visual Studio 2019的C4996错误的两种方式
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
解决visual studio 2019 的C4996错误
error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
@author liuguanglei ittimelinedotnet@gmail.com
@website ittimeline.net
@version
*/
int main(int argc, char* argv[])
{
//定义一个字符串
char str[32] = "";
//将hi string 复制到str中
strcpy(str, "hi string");
printf("str = %s
",str);
system("pause");
return 0;
}
1.16.4 Visual Studio 2019的代码片段
Visual Studio 2019提供了代码片段的功能,其作用就是配置代码片段后,可以通过输入指定的快捷键生成模板代码,这样可以大大提高开发效率。
在后期编写C程序中,需要反复使用到如下的代码片段
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
@author liuguanglei ittimelinedotnet@gmail.com
@website ittimeline.net
@wechat 18601767221
@version
*/
int main(int argc, char* argv[]) {
return 0;
}
首先自定义代码片段文件MyCCode.snippet
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>MyCCodeTemplate</Title>
<Shortcut>mcct</Shortcut>
<Description>c的标准模板代码</Description>
<Author>Microsoft Corporation</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Code Language="cpp">
<![CDATA[#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
@author liuguanglei ittimelinedotnet@gmail.com
@website ittimeline.net
@wechat 18601767221
@version
*/
int main(int argc, char *argv[])
{
system("pause");
return 0;
}]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
然后选择Visual Studio 2019 菜单 工具->代码片段管理器
选择代码片段管理器的语言为C++,然后选中My Code Snippets
然后为导入的代码片段选择一个位置
导入成功
导入成功之后新建任意的C程序源文件,在源文件中输入快捷键mcct就可以生成如下的代码片段
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
@author liuguanglei ittimelinedotnet@gmail.com
@website ittimeline.net
@wechat 18601767221
@version
*/
int main(int argc, char* argv[])
{
system("pause");
return 0;
}
想要了解代码片段的更多内容,请移步官网