现代C语言程序设计之C语言概述

现代C语言程序设计之C语言概述

1.1 信息技术发展趋势

目前信息技术主要经历了互联网移动互联网以及以大数据、云计算、物联网、人工智能、区块链为代表的新兴技术三个阶段。

  • 互联网
    互联网从20世纪90年代逐渐兴起,主要是通过网络连接了世界各地的笔记本、台式机以及背后提供后台数据服务的服务器集群,其中绝大多数笔记本和台式机都运行着Windows,macOS系统,而服务器主要是以类Unix(CentOS,Ubuntu)占据主要市场,依靠互联网成长起来的公司有微软、谷歌、苹果、亚马逊、百度、阿里巴巴、腾讯等等。
  • 移动互联网
    移动互联网主要是通过网络连接了世界各地的移动设备(最典型的就是手机),它们绝大多数都运行着Android,IOS系统,依靠移动互联网公司成长起来的有美团、滴滴、小米、蚂蚁金服。
  • 云计算、大数据
    当然随着用户数据的爆发式增长,以海量数据为基础的大数据、云计算技术在BAT,Apple,Microsoft,Amazon,Google级别的超大型互联网公司有广泛的应用场景。
  • 物联网
    物联网会以手机为中枢,通过网络连接所有智能设备,包括智能家居、汽车、电视等嵌入式设备,目前小米,华为等大厂在智能家居,电视开始布局。
  • 人工智能
    人工智能(AI)作为当前最火爆的技术之一,国内的BAT纷纷开始根据自身核心业务布局,阿里巴巴最早将人工智能应用在电商和物流领域,而百度开发出了对话时人工智能操作系统Dueros和自动驾驶平台Apollo,而腾讯则是在游戏领域应用人工智能。
  • 区块链
    而区块链经历了数字货币、智能合约、和超级账本三个发展阶段,区块链技术可以广泛使用在金融、供应链、物流、公共服务领域,解决互联网的信任问题。
    目前蚂蚁金服已经有大量的区块链应用落地。

1.2 浅谈计算机系统架构

1.2.1 计算机硬件系统

现代计算机是由运算器、控制器、存储器、输入设备、输出设备五大部分组成,它们各司其职,完成了数据的计算、存储、传输任务,下面是它们各个组件的功能介绍:

  • CPU
    也被称为中央处理器,由运算器和控制器组成,其主要作用是数据计算(从内存中获取指令并执行后将结果返回给内存或者写入到磁盘)和控制其他设备(声卡显卡,鼠标键盘)协同工作。目前主流的CPU架构有基于Intel的复杂指令集的X86架构(32位和64位)和手机(ARM指令集),服务器(SPARC指令集)的精简指令集。CPU通过总线(数据总线、地址总线、控制总线)和外部进行交互。
  • 内存
    采用编址存储,其主要作用是用来作为程序的工作区,程序运行时其数据和指令会被加载到内存,断电后数据会丢失。
  • 硬盘
    其主要作用是永久性存储海量数据,分为机械式硬盘和固态硬盘两大类。
  • IO设备
    其主要作用是用来数据的输入输出,常见的输入设备包括鼠标键盘,常见的输出设备包含声卡,显卡,打印机等等。网卡主要是负责数据在网络上的传输。

在后期程序排错时除了考虑程序本身的错误之外还要考虑计算机硬件故障(例如磁盘被写满,网络不通)等问题。

每个计算机组件的IO性能也各不相同,核心组件(CPU,内存,磁盘)都遵循容量越大,IO性能越差,如下图所示,汇总了它们的IO性能。
IO性能汇总

在后期程序优化时,通常需要考虑时间复杂度和空间复杂度的问题。

1.2.2 计算机软件系统

软件出现的作用是实现了人和计算机更好的交互,它是由开发人员采用某种编程语言来编写一系列的指令以及根据不同的业务逻辑产生的数据组成。

1.2.2.1 软件分类

而软件通常被分为系统软件和应用软件

  • 系统软件:
    系统软件有运行在服务器端的Unix,Linux,运行在PC桌面的macOS,Windows;运行在移动设备(手机、电视)的Android,IOS;系统软件通常是负责管理硬件资源,同时给应用软件提供运行平台。

  • 应用软件:
    应用软件有运行在PC桌面、手机端的淘宝、京东、微信、支付宝等等。

1.2.2.2 软件的交互方式

不同平台的软件有不同的交互方式:

  • 服务器端:
    运维开发人员基于命令行的字符界面实现人机交互。
  • PC桌面端:
    开发人员、设计人员、产品经理、普通用户通常是基于图形化界面实现人机交互。
  • 移动端:
    普通用户通常是通过语音,手势触控实现人机交互。

1.2.3 常用应用的软硬件协作实现

计算机程序运行流程如下图所示
计算机程序运行流程

  • 聊天:应用程序监听键盘输入的数据,放到内存,然后传给网卡,通过网络传递给另外一个网卡,再从网卡传到内存,显示在显示器上。
  • 听音乐:应用程序将音乐数据加载到内存之后,然后写到声卡上。
  • 看视频:应用程序将视频数据加载到内存,然后写到显卡上。
  • 读文档: 应用程序将磁盘中的文档数据加载到内存后显示到屏幕上。
  • 写文档: 应用程序将内存中的数据写入到磁盘上。

1.3 程序和指令

  • 程序:
    程序是为了完成某项特定任务(例如聊天,听音乐等等)而使用某种编程语言编写的一组指令序列。
  • 指令:
    令是对计算机进行程序控制的最小单位,由操作码和操作数组成,操作码指的是机器要执行什么操作(例如加减乘除),而操作数就是具体执行的对象(具体的数据以及存放数据的位置),所有指令的集合称为计算机的指令系统,常见的电脑指令系统有Intel X86指令集,常见的手机指令系统有ARM。因此手机上的应用程序不加修改是不能在电脑上直接运行,因为不同的指令集是不兼容的。

1.4 编程语言发展史及其应用场景

1.4.1 编译器与高级语言

首先明确一点,计算机最底层只能识别二进制(010101)的机器指令,那些由汇编语言或者是高级程序设计语言编写的应用程序只是为了方便开发人员理解和维护,这就需要将汇编语言和高级语言翻译成计算机能够理解的机器语言,而编译器或者解释器就是做这个工作的。它的出现让开发人员在编写程序时不用考虑底层硬件的差异性,只需要专注具体业务逻辑的实现即可。

C语言的编译器在各个操作系统之上都有实现,其中Windows系统可以使用Visual C++编译器,而Linux可以使用GCC编译器,macOS可以使用LLVM CLang编译器。

1.4.2 编程语言发展史

任何事物都是从无到有,逐步发展壮大起来的,编程语言也是一样。

计算机程序设计语言经历了机器语言汇编语言高级程序设计语言三个阶段,其特点是使得程序员用编程语言开发、测试、部署应用程序越来越方便、高效。但是是以牺牲效率为代价,但是随着计算机硬件的发展,绝大多数应用场景的效率损失可以忽略不计。

  • 机器语言
    计算机发展的最早期,程序员编写程序采用二进制的指令(010010101)来实现的,而每种CPU都有各自不同的指令系统(Ultra SPARC/Intel X86/ARM),因此在不同的机器上使用不同的机器语言实现。其特点是性能特别高效,而面向机器编程也就意味着不能移植,需要手动处理底层硬件的差异性,而且二进制的指令难以理解和维护。
  • 汇编语言
    随着时代和计算机技术的发展,汇编语言和编译器的出现解决了机器语言需要记住非常多的二进制机器指令的难题,但是还是没有从根本上解决移植性的问题,只是将机器指令转换为易懂的英文单词,然后由编译器编译成机器指令,因为计算机终归揭底只能识别二进制的0001110100110机器指令,而且汇编语言是面向机器的,不同机器(SPARC/Intel X86/ARM)的汇编指令是不相同的。
  • 高级程序设计语言
    高级程序设计语言的高级之处体现在开发人员在编写程序时无需关心计算机底层硬件差异而只需要专注于业务模块实现即可。甚至是可以实现 一次编译,到处运行,这里以Java为例子:通过实现不同平台的JVM,编译生成的字节码文件可以在任意的JVM上运行。
    高级语言通常都需要编译器或者是解释器将源码编译或者解释后执行。

高级语言主要分为面向过程和面向对象两种,其中典型的面向过程编程语言就是C,面向对象的编程语言有Java,C++等等。

1.4.3 编程语言应用场景

如果想知道目前主流的编程语言有哪些,可以访问tiobe 首页获取编程语言排行榜,如下图所示,从Tiobe官网获取最新(2018年12月)的编程语言排行榜的前20名,
编程语言排行榜
根据Tiobe排行榜得知,Java/C/C++分别排在前三名, 随后紧跟着Python,C#,PHP,JavaScript等等,每种语言都有不同的应用场景和擅长的领域,如下表格所示。

编程语言应用场景
C硬件驱动、操作系统、系统软件
C++系统软件、网络通讯、科学计算 、游戏
C#Windows应用,Web应用、游戏
Java大型互联网应用(淘宝、天猫),Android,大数据
Python人工智能、机器学习、自动化运维、数据分析 、图形处理
PHP中小型网站开发
Objective-C/SwiftmacOS,iPhone,iPad应用开发
JavaScript浏览器端、服务端、PC桌面
Go高并发、区块链

那么如果你作为一个野生程序员的初学者,面对如此之多的编程语言,到底应该先从哪门语言上车呢?如果你想深入的学习其他语言和架构相关的知识之前建议熟练掌握C语言。因为C++/Java/C#等编程语言都模仿了C语言。无论哪种语言,都是实现目标的工具,而不是目标本身

1.5 C语言概述

C语言凭借其高效率、良好的移植性、功能强大的特性在操作系统、硬件驱动以及系统应用开发占据广阔的市场。

1.5.1 C语言发展简史

  • 起源
    1972年,贝尔实验室的Dennis Ritch和Ken Thompson在开发Unix操作系统时设计了C语言,该操作系统的90%以上的代码都是由C语言实现,后期的Linux,MacOS,Android,IOS都是基于Unix发展而来的。
  • 标准
    1987年Dennis Ritch和Brian Kernighan编写了The C Programming Language第一版是公认的C标准实现,而没有定义C库。
    而后期ANSI/ISO先后于1990年、1999年和2011年发布了C90标准、C99标准和C11标准,该标准定义了C语言和C标准库。

1.5.2 C语言特性

C语言作为面向过程的高级程序设计语言,能够轻松的实现自顶向下的规划结构化编程模块化设计,这样使得程序结构更加简洁,可扩展性强以及更加容易维护。

而且C语言有着高效、功能强大(嵌套汇编)以及可移植性(标准库可移植)等优点,而且也存在着对系统平台库依赖严重,由于编程风格自由,经验不足也会容易导致出错,编写代码实现周期长,同样的代码在不同的操作系统(或者编译器)下可能会有不同的表现等缺点。

1.5.3 C语言应用场景

C语言偏向操作系统、硬件驱动、底层应用、嵌入式应用开发,硬件驱动的绝大部分实现是由C语言和汇编语言实现的。
目前主流操作系统(Unix,Linxu,MacOS,Windows,Android,iOS)的底层实现都是由C语言和部分汇编实现的。
C++,Java,Python,Swift的编译器或者解释器都是由C语言实现的。
Git,Nginx,Redis,MySQL都是使用C语言实现的,而且都是开放源代码的,可以在GitHub中获取到,
可以通过阅读源码提升自己的设计和编码能力。

1.6 C语言开发环境搭建

1.6.1 C语言开发环境概述

目前主流操作系统(Windows,Linux,MacOS)都有完善的C语言集成开发环境,用于编辑、编译、调试、打包部署C程序,下面表格是各个操作系统对应的集成开发环境说明。

操作系统开发工具
Windows10Visual Studio2017
macOS10.14XCode10
Ubuntu18.04QT5.10

Windows作为世界上最流行的桌面操作系统,最新版本为Windows10 1803,VisualStudio作为Windows上最强大的集成开发环境,可以开发Windows软件,游戏,Web应用等等,最新版本为VisualStudio2017 15.8.9。

Ubuntu作为最受欢迎的桌面版Linux系统之一,推荐采用跨平台的集成开发环境QT来编写C/C++程序。

MacOS平台推荐使用XCode来编写C/C++程序。

除此以外还有些跨平台的C/C++ 开发工具,例如来自Jetbrains公司的CLion以及CodeBlocks也可以用来编写C/C++程序。

1.6.2 Windows10 安装Visual Studio 2017

  1. 官网下载Visual Studio 2017 企业版的在线安装程序,如下图所示
    下载Visual Studio 2017企业版

  2. 启动安装程序
    根据下载的路径查找安装程序并启动,启动完成后会看到如下图所示的程序
    visual studio_installler
    点击继续之后会自动下载组件列表

  3. 选择安装组件
    选择安装组件
    这里选择通用Windows平台开发和使用C++桌面开发两个组件,然后点击右下角的安装,此时Visual Studio 2017会自动下载和安装所需组件,只需要耐心等待即可,如下图所示

    安装选择的组件

  4. 首次启动设置
    安装完成之后需要设置主题和开发模板,还要登录你的microsoft账号,如下图所示
    启动设置

  5. 启动界面
    visual studio 启动界面

  6. 查看安装Visual Studio 2017的版本
    点击帮助菜单,查看关于Microsoft Visual Studio的菜单
    点击帮助菜单,查看关于Microsoft Visual Studio的菜单
    然后会弹出如下版本信息以及包含组件的窗口
    visual studio 2017 版本信息

1.6.3 VisualStudio2017集成GitHub

  • GitHub概述
    GitHub是一个免费、基于Git的开源项目托管站点,全世界许多公司的开源项目(例如Spring,Mybatis,阿里巴巴美团点评等等)的源码都存放在GitHub,个人也可以上传自己的项目到GitHub,可以为自己的就业简历加分。
    如果想要使用GitHub,首先需要访问官网,注册账号后即可使用。

1.下载GitHub插件
启动Visual Studio2017后,通过菜单工具->扩展和更新->

下载GitHub插件
下载GitHub插件

然后选择扩展和更新的联机菜单来搜索GitHub插件,点击下载。如下图所示

选择扩展和更新的联机菜单来搜索GitHub插件,点击下载
选择扩展和更新的联机菜单来搜索GitHub插件,点击下载

下载界面如下图所示

下载示意图
下载示意图

下载成功之后会出现需要重启Visual Studio的提示
重启Visual Studio

  1. 安装GitHub
    在关闭Visual Studio之后,会自动弹出安装界面,如下图所示
    安装界面

    根据提示,选择修改,如下图所示
    选择修改

    安装GitHub插件,如下图所示
    正在安装GitHub插件

    安装成功,如下图所示
    安装成功

  2. 登录GitHub
    首先启动Visual Studio 2017,切换到团队资源管理器的视图,然后点击GitHub的连接,如下图所示
    连接GitHub
    然后会出现登录界面,如下图所示
    登录GitHub
    输入你在GitHub上注册的账号信息即可登录GitHub了,登录成功之后的界面如下图所示
    登录成功之后的界面

  3. 创建代码仓库
    在团队资源管理器视图的GitHub下点击创建菜单,如下图所示
    创建项目
    然后设置仓库名称、描述、本地路径以及Git忽略和许可证信息,如下图所示
    设置仓库信息
    创建完成之后的界面如下图所示
    仓库信息
    GitHub插件会自动创建仓库,并推送到GitHub网站,可以通过地址https://github.com/ittimeline/modern-c-programming 访问到该项目

1.6.4 基于VisualStudio+Github实现C语言helloworld

  1. 创建解决方案
    在团队资源管理器视图下创建解决方案
    创建解决方案

    设置解决方案的项目模板,名称,路径信息,如下图所示
    解决方案配置

    然后再切换到解决方案视图下查看解决方案
    解决方案总览
    解决方案的目录结构说明

  • 源文件用于存放c语言的源代码

  • 头文件用于存放C语言库函数依赖的头文件

  • 资源文件用于存放项目相关的资源

  • 使用Visual Studio 2017实现C语言的helloworld
    首先新建筛选器,并命名为Chapter1

    新建筛选器

    Chapter1

    然后在筛选器下新建helloworld.c文件
    选择添加->新建项
    新建项

    选择基于C++的源文件
    新建helloworld.c
    编辑运行界面如下图所示
    编辑、运行界面
    应用案例(Chapter1/helloworld.c)如下所示

#include <stdio.h>
#include <stdlib.h>

/*
    基于Visual Studio 2017实现的C语言版helloworld
    @author tony ittimeline@163.com
    @version  2018/11/19 15:04:07
*/
void main() {

    printf("hello world with Windows10 & Visual Studio 2017\n");
    //按任意键退出程序,如果不加上 程序执行完成后就会退出
    system("pause");

}

程序运行效果如下图所示
运行效果
如果你想在任何有网络连接的地方使用你的代码,只需要将其推送到远程仓库即可。
首先切换到团队资源管理器,然后点击更改,如下图所示
切换到团队资源管理器
然后输入代码说明的提交信息,如下图所示,再点击全部提交,会将代码提交到本地

代码说明的提交信息
代码说明的提交信息

再输入提交信息,并点击同步和推送后会将代码推送到GitHub

同步
同步

推送
推送

推送成功之后会看到如下提示信息

推送成功
推送成功

1.7 记事本编程实现helloworld

helloworld通常是用来形容学习一门编程语言的入门程序,如果你能够独立把helloworld独立编写、编译、运行成功,那么就算是迈入计算机编程的大门了。

1.7.1 helloworld编写

C语言的源文件后缀名是以.c结尾的,可以使用任意的文本编辑工具(例如记事本,Notepad++,UltraEdit,Visual Studio Code等等,)来编写简单的C程序

这里通过Windows10自带的记事本来编写源程序helloworld.c,源程序如下所示

#include <stdio.h>

void main(){
    printf("欢迎跟光磊一起学习C语言");
}

1.7.2 编译、运行源程序

之前提到过,计算机底层只能识别二进制的机器语言,这里使用C语言编写的源程序无法试别,因此需要一个编译器来将其转换为二进制的机器指令。
各大操作系统(Windows,Linux,macOS)都有C语言编译器的实现,这里以Visual Studio 2017提供的编译器来编译C程序。

首先通过Windows10提供的搜索功能找到 VS 2017的开发人员命令提示符

VS 2017的开发人员命令提示符
VS 2017的开发人员命令提示符

然后使用cd命令切换到C语言源程序的所在路径,接着使用cl命令编译源程序,并运行编译、链接生成的可执行文件helloworld.exe,如下图所示

编译运行C程序
编译运行C程序

1.8 helloworld深入解析

1.8.1 #include预编译指令

如果想要调用函数的某个方法,必须首先包含该方法所在的头文件,例如调用printf()函数,就是位于系统目录C:\Program Files (x86)\Windows Kits\10\Include\10.0.17134.0\ucrt下的头文件stdio.h。如果该文件在当前目录下,使用#include "头文件",如果该头文件在系统目录下,则使用#include <>。编译器查找当前目录下的头文件时如果未找到,还会去系统目录下查找对应的头文件。
#include的作用实际上就是将包含的文件全部复制到源文件中,接下来编写一个C程序来演示#include的作用。

首先在c-core-programming项目的头文件文件夹中新建(Ctrl+Shift+A)一个头文件guanglei.h,C语言中的头文件通常是以.h结尾,如下图所示
guanglei.h
头文件(guanglei.h)如下所示

#include <stdio.h>

/*
     #include预编译指令
    @author tony ittimeline@163.com
    @version  2018/11/19 15:46:07

*/
void main() {

    printf("执行guanglei.h头文件中的内容\n"); //这里的\n表示换行
    getchar(); //等待输入任意字符退出
}

然后在c-core-programming项目的源文件文件夹下创建源文件include.c,该源文件的
源码如下所示

//包含项目路径下的guanglei.h头文件
#include "guanglei.h"

然后运行项目后,发现如下错误
运行错误
其原因是Visual Studio 2017中的单个解决方案下的程序只能有一个入口,即main方法,为了解决这个问题,需要把之前的源程序helloworld.c的main方法重命名为其他函数(例如helloworld_main)即可。
后续章节中给出的代码默认都是带main方法可以独立运行的,但是GitHub仓库中的代码需要确保只有一个main方法才可运行

再次运行程序,程序运行结果如下图所示,也再次表明#include的内容会被复制到源文件中。
程序运行结果

1.8.2 main函数

在Windows下,一个C语言的应用程序如果想独立运行,必须有main函数作为程序的入口,main函数的声明格式如下代码片段所示,所有的程序代码从main函数开始执行。

void main(){

}

其中void表示该函数没有返回值,也就意味着该函数无需使用return关键字表明该函数的返回值。函数的声明格式为

返回类型  方法名(参数列表){
    函数体;

}

其中返回类型可以是C语言的任意数据类型,如果不需要返回,则可以使用void,函数名称见名知意即可,而()中是函数的参数,如果不需要参数则可以使用void表示,而()和(void)是等效的。{}中的内容是函数体,也就是函数执行的具体业务逻辑。

而在Linux下,编译器强制要求main函数返回为int,代码片段如下所示

int main(){

}

1.8.3 return语句

return通常用在函数中,表示退出该函数,这也就意味着return之后的代码都不会被执行。而C语言的编译器比较松散,即使函数在定义时有返回值,而实现没有返回值也不会编译错误,但是却会得到一个错误的结果,应用案例(Chapter1/return.c)如下所示

#include <stdio.h>

/*
    定义两个整数相减的函数,返回两个整数的差
    @author tony ittimeline@163.com
    @version  2018/11/19 15:50:07
*/
int sub(int one, int two) {
    return one - two;
    printf("return之后的语句不会在执行了");
}

/*
    定义两个整数相加的函数,返回两个整数的s和
    @author tony ittimeline@163.com
    @version  2018/11/19 15:50:07
*/
int add(int one, int two) {
    return one + two;
}

/*
    return关键字
    @author tony ittimeline@163.com
    @version  2018/11/19 15:49:07
*/
void main() {
    printf("sub方法调用的结果是%d\n", sub(20, 10));
    printf("add方法调用的结果是%d\n", add(100, 200));
    getchar();

}

1.8.4 断点和调试

在后期程序开发中可能会遇到各种各样的错误,这时我们就需要利用Visual Studio 2017提供的强大的调试功能来查找错误,而且之所以选择Windows10+VisualStudio2017作为C语言的开发环境,也是因为VisualStudio几乎是所有IDE中调试功能最强大的,这样更有利于理解程序的执行流程。

在调试之前首先得下断点,如下图所示
断点
然后在运行程序时,程序会在下过断点的地方暂停,然后可以通过点击如下的箭头,选择每次执行一行,还是每次执行一个方法。
执行方式
还可以通过Visual Studio 2017的菜单 调试->窗口查看内存、寄存器等信息
调试信息
例如这里通过变量的内存地址查看对应存储的数据
内存中的数据
应用程序(Chapter1/debug.c)如下所示

/*
    调试方法
    @author tony ittimeline@163.com
    @version  2018/11/19 16:42:07
*/
void debug_method() {

    printf("使用visual studio的逐过程调试\n");
}

/*
    处理数据函数
    @author tony ittimeline@163.com
    @version  2018/11/19 16:42:07
*/
void handle_data_method() {

    printf("处理数据函数\n");
}
/*
    visual studio的调试功能应用
    通过断点查看内存,逐语句、逐方法调试程序
    @author tony ittimeline@163.com
    @version  2018/11/19 16:42:07
*/
void main() {

    int age = 29;
    printf("整数变量age的地址是%p\n", &age);
    printf("整数变量age的值是%d\n", age);
    debug_method();
    handle_data_method();
    system("pause");
}

1.8.5 注释

为了便于程序的后期维护,C语言支持单行注释//和多行注释/**/,注释用于描述程序的功能,编译器在编译时会将注释的内容删除,需要注意的是多行注释的内容不能嵌套,否则会发生编译错误,应用案例(Chapter1/comment.c)如下所示

#include <stdio.h>
#include <stdlib.h>

/*
    C语言注释和单行注释的使用
    @author tony ittimeline@163.com
    @version  2018/11/19 17:15:07
*/

void main() {

    //打印输出helloworld的两种方式

    printf("hello world \n"); // \n表示换行
    system("echo hello world\n");
    system("pause"); //程序暂停,按任意键退出

}

1.9 Windows常用命令

主流的操作系统(Windows,Linux(Ubuntu),MacOS)及大多数应用程序都提供了基于命令行和图形化界面两种交互方式,而移动端是采用手势触控、语音等进行交互。作为普通用户来讲,图形化界面容易上手,而且交互效果更加直观。

但是作为一个程序员来讲,应该去熟悉各个操作系统的命令行的使用,因为命令行相对图形化界面而言,绝大多数场景下使用更加高效。而且图形化界面本质是对命令行的封装,能用图形化界面实现的基本都可以采用命令行实现。而且在服务器端(CentOS,RedHat)基本上是不会安装图形界面。

Windows系统可以使用快捷键Windows+r调出运行,如下图所示

cmd
cmd

后输入cmd,便可以进入Windows下的命令行终端,如下图所示
dos命令窗口
Windows下常用的文件目录和系统应用相关的命令如下所示,只要开启了终端就可以在终端内通过使用文件目录相关和系统应用相关的命令实现快捷操作。

1.9.1 文件目录相关

命令名称功能描述
dir列出当前目录列表
cd切换目录(只能在根盘符(例如C盘)内切换)
md创建目录
del删除文件
type显示文本文件内容
cls清除屏幕内容
exit退出终端

1.9.2 系统应用相关

命令名称功能描述
notepad记事本
calc计算器
mspaint画图
explore文件资源管理器
timedate.cpl日期和时间
cleanmgr磁盘清理
desk.cpl分辨率设置
powercfg.cpl电源设置
regedit注册表编辑器
msconfig系统配置
mstsc远程连接
firewall.cpl防火墙
appwiz.cpl添加或修改程序
tasklist查看进程列表
taskkill /f /im process.exe关闭指定进程
msinfo系统信息
sticky notes便签
ipconfig查看ip
winver查看windows版本
echo显示文本内容 例如echo %path% 查看系统环境变量
dxdiagDirectX诊断工具

1.10 system函数的使用

system函数是位于系统路径下的stdlib.h头文件中,用于调用各大操作系统的应用程序,如下应用案例所示(Chapter1/system.c)展示了读取用户输入的命令后通过system函数执行调用应用程序。

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <stdlib.h>

/*
    使用system函数执行用户输入的命令
    @author tony ittimeline@163.com
    @version  2018/11/19 17:19:07
*/
void main() {

    printf("请输入你要执行的命令\n");
    //定义数组保存用户输入的指令
    char str[100] = { 0 };
    //使用scanf函数读取用户输入的指令
    scanf("%s", str);
    //执行命令
    system(str);

}

在使用scanf()函数时,需要定义常量#define _CRT_SECURE_NO_WARNINGS来关闭编译器的安全检查。

因为system函数是位于系统路径下的stdlib.h头文件中,也就意味着是可以实现跨平台调用系统下的命令。
需要注意的是虽然system函数是可以跨平台执行,但是system函数中传递的参数是不能跨平台的,以notepad为例,在windows上执行可以打开记事本,但是在ubuntu下执行会报无法识别的命令。
如下应用案例所示,是在Ubuntu下实现的系统命令调用。

#include <stdio.h>
#include <stdlib.h>
int main(){

    printf("请输入你要执行的命令\n");
    //定义数组保存用户输入的指令
    char str[100] = { 0 };
    //使用scanf函数读取用户输入的指令
    scanf("%s", str);
    //执行命令
    system(str);
    return 0;
}

当使用system调用多个应用应用程序时会产生同步的效果,如下应用案例(Chapter1/system_sync.c)所示

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

/*
    system函数的同步特性
    @author tony ittimeline@163.com
    @version  2018/11/19 17:21:07
*/
void main() {
    //system函数的同步特性
    system("notepad"); //当打开记事本之后
    Sleep(5000);//等待5秒钟
    system("tasklist"); //只有自己手动关闭记事本后才会执行查看任务列表
    system("taskkill /f /im notepad.exe");//并没有关闭记事本
}

而如果想要system函数异步执行,只需要传递start参数即可,如下应用案例(Chapter1/system_async.c)所示

#include <stdlib.h>
/*

    如果想异步执行调用多个应用程序,可以加上start参数实现 但是这只针对系统路径下的应用程序有效
    @author tony ittimeline@163.com
    @version  2018/11/19 17:23:07

*/
void main() {
    //加上start参数之后会发现记事本和计算器同时打开,此时异步执行
    system("start notepad");
    system("calc");
}

这里总结下同步和异步
同步:也就是程序只有单条执行流,从上往下开始逐条语句的执行,如果某条语句或者方法执行未完成则会发生阻塞
异步:也就是程序中会有多条执行流,每条执行流随机的执行,不会发生阻塞。

1.11 C语言编程流程

1.11.1 实现打开windows下的记事本,并在5秒之后关闭

实现思路:

  1. 首先使用sytem函数加上start参数异步打开记事本
  2. 然后借助Windows.h头文件的Sleep函数实现程序等待5秒
  3. 最后借助taskkill /f /im notepad.exe 命令关闭记事本。

完整应用案例(Chapter1/open_and_close_notepad.c)实现如下所示

#include <stdlib.h>
#include <Windows.h>
/*
    打开记事本并在5秒之后就关闭
*/
void main() {

    //异步执行
    system("start notepad");

    Sleep(5000);

    system("taskkill /f /im notepad.exe");

}

1.11.2 实现打开windows下的QQ,并根据用户输入的时间关闭QQ

完整的应用案例位于Chapter1/open_and_close_qq.c源文件中。

首先引入相关函数的头文件

#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <Windows.h>

然后定义一个打开QQ的函数openQQ,如下所示
,使用ShellExecuteA()函数可以避免system函数的同步问题,因为system函数的start参数只能作用于不带路径的程序,例如notepad,calc,mspaint。

/*
    打开QQ
    @author tony ittimeline@163.com
    @version  2018/11/19 17:27:07
*/
void openQQ() {
    /*
        函数参数说明
        第一个参数:默认为0
        第二个参数 操作->Open
        第三个参数 命令->这里传递的是QQ的路径,并使用了转义字符
        第四五个参数 默认0,0
        第六个参数 如果是0表示隐式打开,1表示默认打开,3表示最大化打开,6表示最小化打开
    */
    ShellExecuteA(0, "Open", "\"C:/Program Files (x86)/Tencent/QQ/Bin/QQScLauncher.exe\"", 0, 0, 1);
}

然后定义sleep函数,如下所示:使用printf函数给予用户提示输入指定的秒数,使用scanf函数读取用户输入的数据,使用Sleep()函数暂停

/*
    暂停
    @author tony ittimeline@163.com
    @version  2018/11/19 17:27:07
*/
void sleep() {
    int second = 0;
    printf("请输入多久后关闭QQ\n");
    scanf("%d", &second);
    printf("QQ将在%d秒后关闭\n", second);
    int i = 1;
    while (i <= second) {

        Sleep(1000);
        printf("睡了%d秒\n", i);
        i++;
    }
}

最后定义关闭QQ的函数closeQQ,如下所示,只需要使用命令taskkill /f /im QQ.exe,即可关闭QQ。

/*
    关闭QQ
    @author tony ittimeline@163.com
    @version  2018/11/19 17:27:07
*/
void closeQQ() {
    system("start taskkill /f /im QQ.exe");
}

然后main函数中分别引用上述函数即可实现功能

/*
    打开并根据用户输入的时间关闭QQ
    @author tony ittimeline@163.com
    @version  2018/11/19 17:27:07
*/
void main() {
    openQQ();
    sleep();
    closeQQ();

}

通过以上两个案例总结下C语言的编程流程:

  1. 需求分析:分析程序要实现哪些功能
  2. 概要设计:分解步骤,一步一步实现
  3. 编写代码:使用C语言相关特性详细实现
  4. 编译程序:使用编译器将源码编译链接成操作系统上的可执行文件
  5. 运行程序:加载到内存后执行
  6. 测试和调试程序:观察程序的运行过程,是否符合预期,如果不符合,需要更改。
  7. 维护和修改程序:根据不同的业务逻辑来调整程序

1.12 C语言的运行流程

C语言编写的程序如果没有使用特定平台的库(例如Windows.h),那么便可以运行在各大操作系统之上,这里以Ubuntu上的gcc编译器为例,了解C程序的运行流程。

首先编写源程序,推荐使用Ubuntu上的VisualStudio Code编写。

#include <stdio.h>
int  main(){

    printf("hello world with ubuntu18.04&visual studio code\n");

    return 0;
}

C程序运行时首先会进行预编译,目前得知预编译主要是完成以下任务

1 将源文件中包含的头文件的内容复制到源文件中

2 将源文件中采用#define定义的常量值完成替换

3 将源文件中的注释删除。

安装gcc编译器,命令如下所示

$ sudo apt install -y gcc

预编译

然后使用gcc的-E选项预编译helloworld.c,-o是指定预编译输出的文件名,
预编译完成之后使用你喜欢的编辑器(例如Visual Studio Code)查看helloworld.E,便可更加直观的明白预编译具体是做什么事情了。

$ gcc -E helloworld.c  -o helloworld.E

转汇编

C语言的源代码计算机是不能识别的,因此在运行之前需要转换成汇编语言,通过gcc编译器的-S选项来实现转换成汇编,命令如下:

$ gcc -S helloworld.c  -o helloworld.S

编译
计算机最底层只能识别二进制(010101)的指令,编译器会将汇编指令转换成顶层的二进制机器码,可以使用gcc编译器的-c选项来实现编译二进制的机器指令,命令如下:

$ gcc -c helloworld.c -o helloworld.o

windows默认编译生成的是.obj后缀的二进制文件,linux默认生成的是.out后缀的二进制文件。

链接
在编译成机器码之后还不能直接运行,还需要一个链接的过程,就是将C语言的库函数,启动代码以及源码编译后的二进制文件打包到一起组成可执行的二进制文件。
链接不需要加任何参数即可,命令如下。

$ gcc helloworld.c -o helloworld.out

完整的命令如下所示

guanglei@ubuntu:~$ pwd
/home/guanglei
guanglei@ubuntu:~$ cd Desktop/
guanglei@ubuntu:~/Desktop$ ls
helloworld.c  
guanglei@ubuntu:~/Desktop$ gcc -E helloworld.c -o helloworld.E
guanglei@ubuntu:~/Desktop$ gcc -S helloworld.c -o helloworld.S
guanglei@ubuntu:~/Desktop$ gcc -c helloworld.c -o helloworld.o
guanglei@ubuntu:~/Desktop$ gcc helloworld.c  -o helloworld.out
guanglei@ubuntu:~/Desktop$ ./helloworld.out
hello world with ubuntu18.04&visual studio code

如果想查看你的程序引用了哪些系统库,可以使用ldd命令查看,如下所示

guanglei@ubuntu:~/Desktop$ ldd helloworld.out
    linux-vdso.so.1 (0x00007ffcee79e000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7ea52cc000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f7ea58bf000)

C程序完整的流程图如下所示
C语言运行流程

转载于:https://www.cnblogs.com/ittimeline/p/10213275.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值