编程之法-C语言应用开发与工程实践-C语言概述

浅谈计算机系统架构

计算机硬件系统

现代计算机是由运算器、控制器、存储器、输入设备、输出设备五大部分组成,它们各司其职,完成了数据的计算、存储、传输任务,整体架构如下图所示
计算机系统架构

下面是它们各个组件的功能介绍:

  • CPU:也被称为中央处理器,由运算器和控制器组成,其主要作用是用来计算数据(从内存中获取指令并执行后将结果返回给内存或者写入到磁盘)和控制其他设备(声卡显卡,鼠标键盘)协同工作。目前主流的CPU架构有基于Intel的复杂指令集的X86架构(32位和64位)和手机(ARM指令集),服务器(SPARC指令集)的精简指令集。CPU通过总线(数据总线、地址总线、控制总线)和外部进行交互,数据的计算是在CPU内部的寄存器完成的。

  • 内存:采用编址存储,其主要作用是用来作为程序的工作区,程序运行时会被加载到内存,断电后数据会丢失。32位操作系统只能最多使用4G内存(编址的数量2的32次方,已经逐渐被淘汰),64位CPU通常可以使用4G以上的内存(编址可达4G*4G,但是受限于其他组件),单台服务器的内存根据不同的用途通常是16-128G。

  • 硬盘:其主要作用是永久性存储海量(TB或者PB级别)的数据,分为机械式硬盘(HDD)(7500rpm,15000rpm)和固态硬盘(SSD)两大类,门户网站通常是两种硬盘混合使用。

  • IO设备:其主要作用是用来数据的输入输出,常见的输入设备包括鼠标键盘,常见的输出设备包含声卡,显卡,打印机等等。还有个典型输入输出设备:网卡主要是负责数据在网络上的传输。

在程序故障诊断或者做程序性能优化是时除了考虑程序自身之外还需要考虑CPU,内存,磁盘和网卡的”健康状况“。

计算机硬件的IO各层次性能汇总

组件名称延时、带宽
CPU
Cache L1,L2,L30.5-15ns、20-60GB/S
内存30-100ns、2-12GB/S
SSD硬盘10us-1ms、50MB-2GB/S
普通硬盘5-20ms、50-200MB/S
网卡-网卡100us-1ms、10MB-10GB/S

从该表格中可以看出CPU延时(最低)和带宽(最高),而普通硬盘是性能最差的,根据木桶效应,在性能优化时应该优先优化磁盘。

推荐给全栈开发者(前端+后端(服务端)+大数据/云计算)的硬件配置

目前(2018/01)为止能买到的最好的硬件配置的笔记本

Windows/Linux(Ubuntu):Razer/雷蛇 灵刃专业版 RZ09-0166 GTX1080 Pro游戏笔记本电脑

主要硬件配置如下:

硬件名称规格
屏幕17.3英寸16:9 4K屏幕
CPU第七代Intel i7-7820HK 3.9GHz
内存32GB DDR4-2667MHz
硬盘512GSSD/1TSSD
显卡NVIDA GeForce GTX1080显卡 8G显存

推荐先安装Windows10 x64企业版,然后通过安装的VMWareWorkStation14 Pro虚拟机上安装Linux/CentOS7.3、Linux/OpenSUSE42.2和Linux/Ubuntu16.04三个常见的Linux发行版。

而如果要从事MacOS、IOS和WatchOS的应用开发,首选当然是MacBook Pro,主要硬件配置如下(内容来自于苹果官网):

硬件名称规格
屏幕15.4英寸
CPU第七代Intel i7 Turbo Boost 最高可达 4.1GHz
内存16GB 2133MHz LPDDR3内存
硬盘512GB SSD/1T SSD
显卡显卡Radeon Pro 560 图形处理器,配备 4GB

MacBookPro则是安装了最新的MacOS10.13,由于缺少极少数必备软件(例如RedisDesktopManager,WPS),可以通过Mac平台上的虚拟机Parallels Desktop 13 安装了Windows10以及一些Mac平台上缺失的软件。

计算机软件系统

软件的出现实现了人和计算机之间更好的交互,软件是由一系列按照特定顺序组织的指令数据组成。
计算机软件分为系统软件和应用软件两大类,如下列表中包含常见的软件及其运行平台。从交互方式上看经历了字符(命令)交互,图形界面交互和语音手势、重力感应交互的发展过程。

  • 系统软件
人机交互方式系统名称平台
命令行界面Unix(Solaris,HP Unix,AIX),Linux(CentOS)服务器
图形界面Windows10,MacOS10.13,Linux(Ubuntu17.10)桌面
自然交互界面(语音、手势)Android8.0,IOS11嵌入式(手机、汽车、电视)

* 应用软件

类型名称平台
通讯微信、QQ、钉钉Windows,MacOS,Android,IOS
购物淘宝、天猫Android,IOS
办公WPS,OfficeWindows,Mac
支付微信支付、支付宝Android,IOS
外卖美团外卖、饿了么Android,IOS
邮件Gmail、网易邮箱PC,Android,IOS

系统软件主要实现和底层硬件交互,给应用软件提供运行平台。操作系统、编译器等都是属于系统软件,目前主流的桌面操作系统是Windows10,Linux(Ubuntu17),MacOS10.13,主流的移动端操作系统是Android8,IOS11,主流的服务端操作系统是Linux(CentOS7.4,Ubuntu17.10),Unix(HP Unix,Solaris,AIX)。

应用软件主要是运行在PC或者是移动终端的操作系统之上,用于解决工作和生活的各种需求,例如购物(淘宝、天猫、),聊天(微信、QQ),听音乐(QQ音乐、网易云音乐),看电影(爱奇艺、腾讯视频,优酷),收发邮件(网易邮箱,Gmail)和支付(支付宝,微信支付)等等。

而应用软分为BS(Broswer/Server)和CS(Client/Server)两种架构。

其中用于BS架构的Browser端应用开发的语言有JavaScript,Server端应用开发有Java,Python,Go,PHP。

而用于CS架构的Client端(Android,IOS,PC端)的开发语言有Java,Objective-C/Swift/C++,Server端的开发语言有Java。* *

MacOS日常开发中常用的软件列表

软件名称功能描述
Chrome、Firefox、Safari浏览器:Web开发必备
Tower、GitHub DesktopGit版本管理、GitHub桌面客户端
XCodeMacOS/IOS/WatchOS开发必备
SecureCRTLinux SSH Client
PostmanWeb API 测试
AndroidStudioAndroid APP开发必备
IntelliJ IDEAJava开发必备
PycharmPython开发必备
WebStorm前端开发必备
PHPStormPHP开发必备
GoLandGo程序开发必备
DataGrip、Navicat PermiumOracle/MySQL客户端
EdrawMax画图
StartUML建模
PDF ExpertPDF书籍查看
微信、钉钉、QQ即时通讯必备
有道云笔记、印象笔记笔记
简书、GitBook、Mweb写作
TeamViewer远程连接
ParallelsDesktop虚拟机(Mac运行Windows)
CleanMyMac系统管理
WireShark、Charles网络抓包
VPN Plus免费VPN
Axure RP8原型
百度网盘资源云端存储
Dash帮助文档查看器

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

编程语言的发展史

计算机程序设计语言经历了机器语言汇编语言高级程序设计语言的演进,其特点是让程序越来越容易开发、测试和部署,在了解编程语言发展史以及使用场景前需要了解程序到底是什么?

程序是指为了完成一项特定的任务(例如听音乐,看电影,购物)而用某种编程语言(C,C++,Java)编写的指令序列,指令是计算机进行程序控制的最小单位,由操作码(例如+ -)和操作数(例如010101)组成,所有指令的集合称为计算机的指令系统,常见的指令集包括运行于PC、服务器上基于Intel处理器的X86指令集和运行于手机的ARM指令集。不同的指令集系统是不兼容的,这也就导致PC上的程序不加修改,不能到手机上运行,反之亦然。

  • 机器语言
    计算机发展的最早期,程序员编写程序采用二进制的指令(010010101)来实现的,而每种CPU都有各自不同的指令系统(SPARC/Intel X86/ARM),因此在不同的机器上使用不同的机器语言实现。其特点是性能特别高效,而面向机器编程也就意味着不能移植,需要手动处理底层硬件的差异性,而且二进制的指令难以理解和维护。

  • 汇编语言
    随着时代和计算机技术的发展,汇编语言和编译器的出现解决了机器语言需要记住非常多的二进制机器指令的难题,但是还是没有从根本上解决移植性的问题,只是将机器指令转换为易懂的英文单词,然后由编译器编译成机器指令,因为计算机终归揭底只能识别0001110100110机器指令,而且汇编语言是面向机器的,不同机器(SPARC/Intel X86/ARM)的汇编指令是不相同的。

而机器语言和汇编语言的使用场景只有在追求绝对性能的场合(例如控制导弹的发射等等)中使用

如下所示案例演示C语言嵌套汇编语言的案例

#include <stdio.h>

/*
   C语言嵌套汇编的应用案例
  * @author tony ittimeline@163.com
  * @date 2017/10/17 16:40
  * @website www.ittimeline.net

*/
void use_asm() {

    int a, b, c = 0; //初始化声明三个整数变量并赋值为0

    a = 4; 
    b = 6;

    printf("before a=%dtb=%dtc=%dn", a, b, c);

    _asm {

        //将变量a的值赋值给eax寄存器
        mov eax, a;
        //将变量b的值和寄存器eax相加
        add eax, b;
        //将相加的结果赋值给c
        mov c, eax;
    }

    printf("after  a=%dtb=%dtc=%dn", a, b, c);


}

/*主程序的入口
*     @author tony ittimeline@163.com
*     @date 2017/10/17 16:38
*     @website www.ittimeline.net
*/
int main() {

    use_asm();
    getchar();

}
  • 高级程序设计语言

    高级程序设计语言的高级之处体现在开发人员在编写程序时无需关心硬件差异只需要专注于业务模块实现即可。甚至是可以实现 一次编译,到处运行(Java通过实现不同平台的JVM,编译生成的字节码文件可以在任意的JVM上运行)。高级语言通常都需要编译器或者是解释器将源码编译或者解释执行。高级语言主要分为面向过程和面向对象两种,其中典型的面向过程语言就是C,面向对象的编程语言有Java,C++等等。

先看下目前(2018年1月)世界上主流语言的排行榜的前20位,数据来源于tiboe
tiboe

目前C语言排行第二, 同时C/C++的市场份额是超过Java的。

主流编程语言的应用场景介绍

目前主流的语言主要有Java/C/C++/C#,Pyhton,PHP,JavaScript,Swift,Objective-C,Go语言,它们拥有各种不同的使用场景。

编程语言主要是用来编写软件,而开发软件的类型主要分为系统软件和应用软件,系统软件主要有Windows,Linux(Ubuntu,CentOS,OpenSUSE),MacOS,Android,IOS,通常使用的系统软件开发语言为少量的汇编以及大量的C语言。

目前在典型的中小型互联网公司的应用软件的后台业务逻辑通常都是采用Java或者Python,C/C++/Go实现。前台展示通常包含PC,H5,Android,IOS和微信公众号、微信小程序。

信息技术发展方向

目前国内一线城市(北上广深)中的一线互联网企业主要有阿里巴巴、腾讯、百度、平安、滴滴、新美大等等,他们所涉及的领域包括电商、社交、游戏、人工智能、金融、出行、团购外卖等等。

而这些领域在技术上的实现通常会涵盖移动端、前端和服务端

移动端主要以Android,IOS为主,而开发Android App必须熟练掌握Java/Kotlin技术栈,开发IOS必须掌握Objective-C/Swift技术栈。

前端主要以HTML5+CSS3+JavaScript/ECMAScrip为基础的bootstrap,jQuery,AngularJS,VueJS,ReactJS,NodeJS,Webpack等框架的技术栈。

服务端主要以Linux(CentOSUbuntu)为主,如果想做服务端底层开发,必须熟练掌握C/C++技术栈,如果想做服务端应用层开发必须掌握Java/Python/Go技术栈。

游戏主要是Cocos2d-x和Unity3d,主要开发语言是C++和C#。

前言的方向是人工智能(Python)以及区块链(Go),物联网

大数据以及云计算因为是以海量数据为基础的,只有在巨头(Google,Amazon,Facebook,Apple,Microsoft,Alibaba,Tencent,Baidu)公司才会有用武之地

C语言概览

起源

1972年,贝尔实验室的Dennis Ritch和Ken Thompson在开发Unix操作系统时设计了C语言,该操作系统的90%以上的代码都是由C语言实现,后期的Linux,MacOS,Android,IOS都是基于Unix发展而来的。

特点

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

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

应用场景

偏向操作系统、硬件驱动、底层应用、嵌入式应用开发

硬件驱动的绝大部分实现是由C语言和汇编语言实现的。

目前主流操作系统(Unix,Linxu,MacOS,Windows,Android,IOS)的底层实现都是由C语言和部分汇编实现的。

C++,Java,Python,Go,Swift的编译器或者解释器都是由C语言实现的。

Git,Nginx,Redis,MySQL都是使用C语言实现的,而且都是开放源代码的,可以通过阅读源码提升自己的设计和编码能力。

C语言还可以开发游戏引擎、图形界面、网站后台的程序。

标准

1987年Dennis Ritch和Brian Kernighan编写了The C Programming Language第一版是公认的C标准实现,而没有定义C库。

而后期ANSI/ISO先后于1990年、1999年和2011年发布了C90标准、C99标准和C11标准,该标准定义了C语言和C标准库。

如果没有特殊说明,后面的样例代码都采用C11标准实现。

精髓

32个关键字(赋予了特殊的含义)+9种控制语句+34种运算符

学习C语言的好处

学完C语言可以快速掌握其他常用语言,例如C++,C#,Java,Python,Objective-C,Go,因为这些语言在许多方面都模仿了C语言,而且必须深入掌握C语言,才能对其他语言的底层实现有比较深入的了解。

C语言是目前众多编程语言中综合性能最好的编程语言

软件生命周期

目前典型的BS或者CS应用开发的主要流程包括需求分析、概要设计、开发、测试、运维部署等过程。

需求分析

主要是由产品经理和运营或者市场部门明确产品的主要需求后编写PRD文档以及设计产品架构

概要设计

由架构师和公司的前后端开发以及UI设计人员在产品经理的PRD文档基础之上进行设计以及文档编写

开发

由前后端人员在概要设计的基础之上进行产品的详细实现,通常采用短周期,快速版本迭代的敏捷开发模式

测试

等待前后端部分模块开发完成之后在测试环境,BT环境进行黑盒测试、白盒测试以及压力测试、系统集成测试等等

部署运维

运维人员在等待系统测试完成之后部署系统所需要运行的环境(数据库、服务器等等)以及产品本身

主流操作系统的C语言开发环境搭建

开发环境介绍

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

操作系统开发工具
Windows10VisualStudio2017
Linux(Ubuntu17.10)QT5.10
MacOS10.13XCode9.2

Windows作为最流行的桌面操作系统,最新版本为Windows10,VisualStudio作为Windows上最强大的集成开发环境,可以开发Windows软件,游戏,Web应用等等,最新版本为VisualStudio2017。后续的C语言应用案例如果没有强调集成开发环境,默认就是基于Windows10+VisualStudio2017上实现的C程序。

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

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

无论哪种平台和集成开发环境,我们都应该数量掌握的是C语言的精髓,集成开发环境的本质只是方便程序员开发、测试和部署程序,提高了工作效率。

每个操作系统下的集成开发环境的编译器实现的C语言细节不完全相同,同样一段代码在不同的系统下执行可能会有不同的效果,

常用的C语言编译器有VisualStudio的C语言编译器(cl.exe)和Linux系统的GCC编译器。

接下来将在各大平台安装并使用IDE来编写helloworld

Windows10+VisualStudio2017的安装与使用

Windows10安装VisualStudio2017

在安装好Windows10创意正式版和从VisualStudio官网下载安装文件之后便可以搭建基于Windows10和VisualStudio2017的C语言开发环境,详细的流程如下图所示:

1 双击下载的VS_Enterprise.exe文件

2 同意条款,继续

3 选择VisualStudio的安装组件并安装

4 开始下载组件并安装
此过程耗时较长,因机器的性能而异

5 完成安装并启动

6 登录VisualStudio2017
如果没有Microsoft账号,去官网注册一个即可

7 启动界面

VisualStudio2017实现HelloWorld

1 启动VisualStudio后新建项目
新建项目

2 设置项目名称、类型、保存的路径信息
项目设置

3 helloworld实现原码如下:

#include <stdio.h> 

/*
    windows系统使用VisualStudio2017实现helloworld
    @author tony ittimeline@163.com
    @date 2017/10/17 06:45
    @website www.ittimeline.net 
*/
int main(void) {

    printf("hello world in windows10 with visualstudio2017n");
    getchar(); //等待输入任意字符后退出程序
    return 0;
}

在写好源代码之后便可以运行程序,VisualStudio提供了一键编译运行C程序的功能,如下图所示

VisualStudio编译运行菜单

源程序helloworld.c编译运行后的结果示意图如下所示
hello world result

VisualStudio2017的使用注意事项

  1. 一个解决方案下可以创建多个项目,当运行指定的项目前需要设置该项目为启动项目

  2. 运行程序时确保整个解决方案下只有一个main方法,不然无法运行

  3. 当前的解决方案下,源文件是不能够重名。

Ubuntu17+QT5.9的安装与使用

Linux版本QT5.9下载

QT5.9下载地址:http://download.qt.io/official_releases/qt/5.9/5.9.1/qt-opensource-linux-x64-5.9.1.run只需要将该地址复制到浏览器中即可下载。

如下图所示:download address

下载中:
downloading...

QT5.9安装

1 打开Ubuntu的终端程序
open termial

2 切换root用户

tony@ubuntu:~$ su root
Password:

3 进入下载目录并查看文件

root@ubuntu:/home/tony# cd Downloads/
root@ubuntu:/home/tony/Downloads# ls -al
total 1008576
drwxr-xr-x  2 tony tony       4096 Aug 10 13:10 .
drwxr-xr-x 17 tony tony       4096 Aug 10 13:15 ..
-rw-rw-r--  1 tony tony 1032768780 Aug 10 13:10 qt-opensource-linux-x64-5.9.1.run

4 执行安装

发现权限不允许,使用chomd u+x添加执行权限

root@ubuntu:/home/tony/Downloads# ./qt-opensource-linux-x64-5.9.1.run #执行安装
bash: ./qt-opensource-linux-x64-5.9.1.run: Permission denied
root@ubuntu:/home/tony/Downloads# chmod u+x qt-opensource-linux-x64-5.9.1.run 
root@ubuntu:/home/tony/Downloads# ./qt-opensource-linux-x64-5.9.1.run 
QStandardPaths: wrong ownership on runtime directory /run/user/1000, 1000 instead of 0

5 安装启动界面
安装启动界面

6 跳过登录
跳过登录
7 进入Setup
进入Setup
8 选择安装路径
选择安装路径
9 选择安装组件
选择安装组件

10 同意License
同意License

11 准备安装
准备安装
12 安装进度查看
 安装进度查看

13 完成安装
完成安装

14 启动界面
启动界面

Linux下C语言的helloworld实现

1 New Project

2 Non-Qt Project
Plain C Application

3 Project Location
设置项目名称和项目路径

4 Define Build System

5 Kit Selection

6 Project Management

HelloWorld源码:

#include <stdio.h>

int main()
{
    printf("Hello World!n");

    getchar();
    return 0;
}

7 程序运行结果

MacOS10.13+XCode9的安装与使用

由于MacOS10.13系统自带了XCode9,因此安装过程就不在赘述了。

MacOS10.13使用XCode开发helloworld

1 创建项目
new project

2 选择项目模板

3 设置项目信息

4 添加版本控制

自动生成的源码如下:

//
//  main.c
//  CPrimerPlus
//
//  Created by tony on 2017/10/20.
//  Copyright © 2017年 tony. All rights reserved.
//

#include <stdio.h>

int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!n");
    return 0;
}


5 编译并运行

C语言的helloworld深入理解

C语言编写应用程序可以采用任意的编辑器(例如跨平台的SublimeText和Atom和之前介绍的各个主流操作系统的集成开发工具)来编写,通常编译器要求C程序的源代码文件是以.c作为源文件扩展名。
helloworld.c源代码文件的代码如下:

#include <stdio.h>

/*
 * C语言的helloworld实现
 * @author tony ittimeline@163.com
 * @date 2017/10/15 17:13
 * @website www.ittimeline.net
 */
int main(void){
    printf("Hello Worldn");
    return 0;
}

Windows平台编译和运行helloworld

环境说明: Windwow10创意正式版+VisualStudio2017

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

开发人员命令提示符

然后采用Visual Studio 2017提供的cl命令来编译源文件,并运行helloworld.exe
,如下图所示
windows_helloworld_compile_run

Linux平台编译和运行helloworld

环境说明 Linux(Ubuntu+vim)

首次安装Ubuntu系统需要先给root用户设置密码,命令如下

tony@ubuntu:~/Desktop$ sudo passwd root
[sudo] password for tony:
Enter new UNIX password: 
Retype new UNIX password:

passwd: password updated successfully

然后安装vim编辑器,命令如下

root@ubuntu:/home/tony# apt install vim 
root@ubuntu:~# su tony
 #切换回tony用户
tony@ubuntu:~$ cd Desktop/
tony@ubuntu:~/Desktop$  #返回到桌面

然后使用vim编辑源文件代码

tony@ubuntu:~/Desktop$ vim helloworld.c

此时vim默认处于命令模式,需要输入字符 i 来进入编辑模式,编辑的内容如下:

Ubuntu helloworld

编辑完成之后按esc :wq便可以保存文件并退出vim编辑器,然后使用 gcc编译器编译源文件,再运行程序。

tony@ubuntu:~/Desktop$ gcc -o helloworld.o helloworld.c

tony@ubuntu:~/Desktop$ ./helloworld.o 
hello world in linux(ubuntu)

编译和运行注意事项

  1. C语言编写的源代码如果没有依赖特定的系统平台库可以在任何安装C编译器的平台上编译、链接后运行(一次编写,到处编译 ),
    但是Windows上编译后的C程序不能在类Unix操作系统(Unix,Linux,MacOS,Android,IOS)上执行。
  2. 32位Linux操作系统上编写的程序也不能到64位Linux上运行,Windows上的32位应用程序可以到64位Windows系统上运行,Windows 64位系统兼容32位的应用程序。
  3. 各个操作系统的可执行程序的格式是不一样的,Windows通常是.exe结尾,而Linux通常是不区分文件后缀名。
  4. C语言的编译器编译程序时比较松散,在编译正确的情况下也不保证程序正确,因此我们在编写程序时必须要严格遵守C语言规范。

  5. 在使用VisualStudio编写基于控制台的C程序时,通常最后一行需要加入getchar()函数或者调用system(“pause”),不然程序会运行后关闭控制台,不方便查看程序的执行结果。

helloworld详解

#include <stdio.h>

/*
 * C语言的HelloWorld实现
 * @author tony ittimeline@163.com
 * @date 2017/10/15 17:13
 * @website www.ittimeline.net
 */
int main(void){
    printf("Hello Worldn"); //打印输出HelloWorld并换行 
    return 0;
}

#include <stdio.h>

在预编译期间将位于系统路径下的stdio.h头文件的内容复制到helloworld.c中,而stdio.h头文件中包含了c语言标准库函数用于数据的输入输出实现的函数声明,如果想调用某个函数(例如printf),必须将函数所在的头文件使用#<头文件的名称> 包含到源文件中。

#include的本质说明

环境说明: Windows10+VisualStudio2017

首先自定义头文件include.h ,头文件内容如下

#pragma once
#include <stdio.h>

void main(void) {

    printf("hello world in Visual Studio 2017 With Windows10 n");

    getchar();//等待输入任意字符退出 
    return;
}

然后定义源文件include_sample.c,源文件内容如下

#include "include.h"

编译运行的结果得知include.h的内容在预编译时已经复制到include_sample.c中,输出如下结果:
#include原理
关于头文件的查找原则说明

当头文件位于当前项目的路径下可以使用#include “头文件名”,系统会先在当前目录下查找头文件,如果没有找到则会在系统目录下查找头文件 ,而系统路径下使用#include <头文件名> ,通常情况下”“可以替换<>

C语言注释

注释用于描述程序的功能说明以及相关信息,增加代码的可读性,便于后期的维护,C语言支持单行注释(//)和多行注释(//),其中需要注意的是多行注释不能嵌套使用**,否则会发生编译错误。

优秀的程序员应该多写注释,因为在程序开发过程中,如果业务逻辑越复杂,注释就显得尤为重要,因为可能过了一段时间(几个月)后如果不仔细阅读代码,就可能忘记程序的业务功能(好记性不如烂笔头)。

在程序排错的过程中也可以通过注释部分代码来调试程序。

编译器在编译程序时,会删除注释的内容。

单行注释和多行注释的案例如下代码所示

#include <stdio.h>

/*
 * C语言的HelloWorld实现
 * @author tony ittimeline@163.com
 * @website www.ittimeline.net
 * @date 2017/10/15 17:13
 */
int main(void){
    printf("Hello Worldn"); //打印输出HelloWorld并换行 
    return 0;
}

C语言main函数

最简单的写法如下所示,C语言的main函数如果兼容C++的编译器,必须声明成返回类型为int,并返回对应的0或者-1,通常情况下return 0表示程序执行成功,return -1表示程序执行失败。

int main(void){

    //待执行的业务逻辑 
    return 0;
}

如果不需要兼容C++, 可以声明成返回类型为void

void main(void){

  //待执行的业务逻辑 
}

如果想自定义函数完成特定的业务逻辑 ,C语言函数声的格式为:

返回值类型  函数名称(参数列表){
    //函数体 也就是函数需要完成的功能
    return 返回值类型的值;
}

main函数是一个特殊的函数,所有的代码从此处开始执行,而且一个C程序只能有一个main函数 ,如果没有main函数编译会出错。

而且每一个语句都要使用分号(;)作为结束

返回值类型可以是C语言支持的数据类型,后续会深入学习

函数或者变量命名需要遵守以字母开头,可以包含字母和数字以及下划线,C语言严格区分大小写

(参数列表) 表示程序运行时需要传递的数据,例如函数如果为了完成两个整数的相加就需要传递两个整数,void表示没有参数。

{函数体}中包含了程序需要完成的业务逻辑,函数体中也可以有多个代码块({}),如下图所示。
code block

printf函数

该函数用于向标准输出设备格式化输出数据,除了之前演示的输出到终端(默认),当然还可以输出到文件,网页,打印机等等。

因为该函数的声明位于 系统路径下的stdio.h头文件中,需要使用#include包含进来后使用,r如下应用程序所示。

#include <stdio.h>

int main(){

    printf("hello worldn"); //n表示转义字符 实现换的功能 
    return 1;
}

当printf输出到网页时,需要提前声明Content-Type,如下应用案例所示

#include <stdio
/*
 使用printf输出数据到网页
@author tony ittimeline@163.com
@date 2017/12/03 13:07
@website www.ittimeline.net
*/

int main() {


    printf("Content-ype:text/htmlnn"); //声明HTML语言
    printf("我的第一个网页应用程序");
    return 0;
}

然后把编译生成的exe程序名称改成homepage.cgi,放到Apache服务器的cgi-bin目录下,启动服务器,然后通过浏览器访问127.0.0.1/cgi-bin/homepage.cgi,如下图示:
printf_cgi

return语句

return 表示退出函数,而return 0 表示方法返回0,这里return 0是返回给操作系统系统执行成功,而通常返回-1表示程序运行失败。return语句之后的语句都不会执行

#include <stdio.h>
/*
 * return的案例
 * return之后的代码都不会执行
 @author tony ittlimeline@163.com
 @date 2017/10/18 23:10
*/
int main(void ) {


    printf("before returnn");
    getchar();//等待输入任意字符退出
    return 0;

    printf("after return");
}

return

return语句还可在普通函数中使用,可以声明一个变量保存函数执行的结果。

#include <stdio.h>

/*
*    定义一个函数,返回两个整数相加的结果
*    @author tony ittimeline@163.com
*    @date 2017/10/31/ 11:24
*    @website www.ittimeline.net
*/
int add( int one,int two) {

    return one + two;
}

/*
 *    return的使用
 *    @author tony  ittimeline@163.com
 *    @date 2017/10/31/ 11:24
 *    @website www.ittimeline.net
*/
int main() {

    //定义变量保存调用add方法的执行结果,并传递两个整数参数 
    int result = add(10,20);

    printf("10 + 20 = %dn",result);

    return 0;
}

使用VisualStudio2017调试程序

在程序员的生涯中除了编码以外,绝大部分工作都是在优化程序(通过版本迭代),解决QA团队测试出来的各种Bug,因此我们必须学会调试程序,通过下断点中断程序,观察程序执行过程中变量的值是否符合预期,如果不符合,则需要修改或者优化程序逻辑。

VisualStudio2017的下断点和调试功能如下所示

首先设置断点,然后按快捷键F5启动调试
visual studio breakpoint

可以通过VisualStudio提供的查看内存、寄存器、反汇编等功能更好的获取程序的运行时信息
debug

C程序的运行流程

这里采用Ubuntu17.10+vim的环境来介绍C语言的运行流程,C语言从源文件到程序运行之前需要经过预编译、转汇编、编译、链接四个步骤,可以通过Ubuntu的gcc编译器的各个选项来了解每个流程的详细细节。

预编译

目前得知预编译主要是完成以下任务

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

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

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

使用gcc编译器的-E选项编译后可以得知结果,命令如下

tony@ubuntu:~/Desktop$ gcc -E helloworld.c  -o helloworld.e

-o表示指定程序的文件名称,如果不加默认文件名为a.out

转汇编

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

tony@ubuntu:~/Desktop$ gcc -S helloworld.c  -o helloworld.S

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

tony@ubuntu:~/Desktop$ gcc -c helloworld.c  -o helloworld.o

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

链接

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

tony@ubuntu:~/Desktop$ gcc  helloworld.c -o helloworld.out
tony@ubuntu:~/Desktop$ ./helloworld.out 
hello world in ubuntu17

使用ldd命令查看程序引用的库

tony@ubuntu:~/Desktop$ ldd helloworld.out

linux-vdso.so.1 =>  (0x00007ffc4b7ce000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2924816000)

/lib64/ld-linux-x86-64.so.2 (0x00007f2924df8000)

如下图所示展示了一个C程序从编辑到运行中间所包含的所有流程
C语言的完整运行流程

Windows常用命令

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

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

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

运行

后输入cmd,便可以进入Windows下的命令行终端,如下图所示
Windows命令行终端

Windows下常用的文件目录和系统应用相关的命令如下所示,只要开启了终端就可以在终端内通过使用文件目录相关和系统应用相关的命令实现快捷操作。

文件目录相关

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

系统应用相关

命令名称功能描述
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
echo显示文本内容 例如echo %path% 查看系统环境变量
dxdiagDirectX诊断工具

使用system函数调用各大操作系统的命令

system函数调用windows系统命令的案例

system函数是位于系统路径下的stdlib.h头文件中,用于调用各大操作系统的应用程序,如下所示,是使用system函数调用windows平台的记事本应用程序。

#include <stdio.h>

/*

 system函数调用windows平台的记事本应用程序
 @author tony ittlimeline@163.com
 @date 2017/10/22 14:38
 @website www.ittimeline.net
*/
int main(void ) {

    //执行调用windows的记事本应用程序
    system("notepad");


    getchar();
    return 0;
}

实际上system函数可以调用各大平台基于终端的应用程序,但是需要注意的是,system函数是可以跨平台运行的,但是system函数中传递的程序可能不是跨平台的,例如notepad在windows上是记事本,而在linux系统上就仅仅只是字符串了。

当然可以使用windows提供的WinExec函数实现和system一样的功能,如下所示:

#include <Windows.h>

/*

  WinExec函数调用windows平台的记事本应用程序
  @author tony ittlimeline@163.com
  @date 2017/10/22 14:38
  @website www.ittimeline.net
*/
int main(void ) {

    //执行调用windows的记事本应用程序
    WinExec("notepad",SW_NORMAL);


    getchar();
    return 0;
}

为了提高程序的可扩展性,通过使用c语言的scanf函数读取用户输入的命令,然后再通过调用system命令执行,代码如下:

#define _CRT_SECURE_NO_WARNINGS #关闭编译器的安全检查功能
#include <stdlib.h>
#include <stdio.h>


/*
    定义一个函数用于读取用户输入的指令并执行
    @author tony ittimeline@163.com
    @date 2017/10/22 15:31
*/
void exec(char str[]) {

    scanf("%s", str);

    system(str);
}

/*
    通过scanf函数读取用户输入的指令,然后再调用system函数执行的案例
    @author tony ittimeline@163.com
    @date 2017/10/22 15:22
*/
int main(void) {
    //定义一个字数组,数组中的每个元素初始化位0
    char str[100] = {0};
    printf("请输入你要执行的命令n");

    exec(str);
    return 0;
}

system函数的同步与异步

首先理解下同步和异步的概念

同步:单条执行流,从上往下进行执行,一个一个的执行,不允许跨越执行

异步:多条执行流,从上往下进行执行,每条执行流随机执行,没有跨越的概念

system函数的同步异步操作:

命令字符串没有start–>同步执行–>单条执行流–>发生阻塞特点

命令字符串含有start–>异步执行–>多条执行流–>没有阻塞特点

system函数的异步等同于:另起一个命令行窗口执行该命令,和当前命令行不属于同一个执行流

#include <stdio.h>
#include <stdlib.h>
/*
  system函数的同步特性    
  @author tony ittimeline@163.com
  @date 2017/10/22 15:46
  @website
*/
int main() {

    system("notepad");//打开记事本 
    system("calc&pause");//当执行打开记事本关闭后打开计算器并暂停

    return 0;
}

运行程序会发现,当notepad打开之后并不会同时去打开计算器,此时就进入了同步状态,只有当notepad关闭之后程序才会执行打开计算器。而如果想异步打开记事本和计算器,只需要在sytem方法传递的参数前面加上start参数即可,代码如下所示。

但是start只能执行系统路径下的应用,如果是其他的应用,需要先进入指定目录再异步执行。

#include <stdio.h>
#include <stdlib.h>
/*
  system函数的异步 通过添加start参数实现    
  @author tony ittimeline@163.com
  @date 2017/10/22 15:46
  @website
*/
int main() {

    system("notepad");//打开记事本
    system("start calc&pause");//给打开的程序前面加上start记事本和计算器会一起打开

    return 0;
}

如下应用程序所示,借助C语言的死循环案例深刻理解异步的特性案例

#include <stdlib.h>
/*
    使用死循环深刻认识system函数的异步特性
    @author tony ittlimeline@163.com
    @date 2017/10/22 14:40
    @website www.ittimeline.net
*/
int main() {

    while (1) {


        system("start notepad");
    }

}

system函数调用Linux(Ubuntu17.10)系统的命令案例

环境 Ubuntu17.10+vim

运行下面的程序时,可以输入gedit,date,vim,top等Ubuntu17.10系统下的程序进行测试

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

/*
  system函数调用Linux(Ubuntu17.10)版的实现
  @author tony ittimeline@163.com
  @date 2017/10/22 15:46
  @website
*/

int main(){

    char str[100]={0}; 
    printf("please input your application n");
    scanf("%s",str);
    system(str);
    return 0;

}

system函数调用macOS系统命令的案例

环境 macOS+XCode9.1

因为macOS和Ubuntu是近亲(都是源自于linux),Ubuntu上的许多系统命令在macOS上也是可以使用的,

如下所示的程序展示了使用system函数调用iTunes,微信,QQ和地图。只要使用open加上应用程序的完整路径即可

//
//  sytem_call.c
//  CPrimerPlus
//
//  Created by tony on 2018/1/15.
//  Copyright © 2018年 tony. All rights reserved.
//

#include <stdlib.h>

int main(){
    //打开iTunes
    system("open /Applications/iTunes.app");
    //打开微信
    system("open /Applications/WeChat.app");
    //打开QQ
    system("open /Applications/QQ.app");
    //打开地图
    system("open /Applications/Maps.app");
    return 0;
}

C语言模块化编程

模块化编程就是通过使用函数来组织模块,不同的函数完成不同的功能,这样程序不仅逻辑清晰,利于维护,也方便扩展。

接下来将通过一个案例了解C语言的模块化编程的实现

需求:完成Windows下的QQ程序的启动,在等待指定的时间(以秒为单位)后,退出QQ程序。

设计算法:分别定义函数来实现打开QQ,暂停和关闭QQ

详细设计:QQ的启动可以调用system函数传入QQ安装的路径即可,等待指定的时间可以通过读取用户输入的秒数,可以通过scanf函数和Windows的Sleep函数来暂停。退出程序可以使用taskkill /f /im 命令来实现。

完整的实现如下所示

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

/*
  定义方法启动QQ
 @author tony ittimeline@163.com
 @date 2017/10/22 19:07
 @website www.ittimeline.net
*/
void openQQ() {

    system(""C:/Program Files (x86)/Tencent/QQ/Bin/QQScLauncher.exe"");
}

/*
    关闭QQ
    @author tony ittimeline@163.com
    @date 2017/10/22 19:08
    @website www.ittimeline.net

*/
void closeQQ() {

    system("taskkill /f /im QQ.exe");
}

/*

    读取用户输入的时间
    @author tony ittimeline@163.com
    @date 2017/10/22 19:08
    @website www.ittimeline.net

*/
int sleepSecond() {

    int second = 0;
    printf("请输入多少秒后退出QQn");
    scanf("%d",&second);
    printf("QQ将在%d秒后退出n",second);
    return second;

}
/*

    程序入口
    @author tony ittimeline@163.com
    @date 2017/10/22 19:05
    @website www.ittimeline.net
*/
int main() {

    openQQ();
    int second = sleepSecond();
    Sleep(second*1000);//Sleep默认传递的是毫秒 因此这里乘以1000转换为秒钟

    closeQQ();

    return 0;
}

Windows系统中采用system()函数执行系统当中的应用程序的时候,需要注意是否有(x86)标识的文件夹,如果是(x86)标识的文件夹,那么说明该操作系统就是X64位的操作系统安装的32位应用程序,因此路径(x86)前面应该填充一个空格,否则不能正常的执行程序,例如这里的system函数就无法调用QQ。


版权声明:本文为ITTechnologyHome原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。