【C++学习之路】第二章——C++基础语法学习(1)之黑客攻击系统

2 C++基础语法学习(1)之黑客攻击系统

遵循原则:从实战中学习知识
法律声明:本系统仅能攻击自己搭建的网站,作为学习使用,不会触犯法律
time:2020.01.20
author:姜海天
IDE:VS C++2010 Express

2.0 VS C++ 2010 Express的使用

2.1 子项目一:功能菜单

2.1.0 本节大纲与知识清单

2.1.0.1 大纲

大纲是最重要的,一定要仔细阅读,不可忽略!

  • 2.1.1~2.1.7是项目设计、实现、测试与优化部分,直接采用了数据结构知识,先不用管这是什么,先接受他的标题与设计过程。
  • 2.1.8及其后面的小节是知识点讲解部分

2.1.0.2 知识清单

本子项目涉及到的知识点如下

  • C++的知识
  • 数据结构的知识

2.1.1 项目需求

设计一个功能菜单,实现黑客攻击系统基本信息的输出
要求的必要信息: 实现网站404攻击,网站篡改攻击,DNS攻击,服务器重启攻击,且记录下来攻击记录

2.1.2 需求分析&整体架构设计

整体架构
设计一个能够输出信息的菜单

2.1.3 ADT模型构建(抽象)

ADT模型1:输出需求中所要求的信息
1. 网站404攻击
2. 网站篡改攻击
3. DNS攻击
4. 服务器重启攻击
5. 查看攻击记录

2.1.4 实现抽象

本项目过于简单,没有这一项

2.1.5 实现

HackerFuncMenu.cpp
#include 
#include 
using namespace std;

int main()
{
	std::cout << "1. 网站404攻击" << std::endl;
	std::cout << "2. 网站篡改攻击" << std::endl;
	std::cout << "3. DNS攻击" << std::endl;
	cout << "4. 服务器重启攻击" << endl;
	cout << "5. 查看攻击记录" << endl;
	
	system("pause");
	return 0;
}

对于初学者并不知道我在干什么,没有关系,请先接受这些内容,后面的小节会详细讲解这些内容。

2.1.6 单元测试

这里只有一个ADT模型的实现,并且由于是初学,所以只是借用了软件工程中单元测试的名字,并不是标准的单元测试,只是单纯的验证程序结果的正确性
【C++学习之路】第二章——C++基础语法学习(1)之黑客攻击系统_第1张图片
显然,程序运行结果是正确的,并且是符合要求的。

2.1.7 结果评价与程序优化

2.1.7.1 正确性

显然,程序的结果是正确的

2.1.7.2 效率

对于此程序,效率是不够高的,并且有一些冗杂的没有必要的代码。


思考:什么是高效率的程序?

在计算机工程中,需要综合考量时间复杂度和空间复杂度。
时间复杂度:程序耗时多少
空间复杂度:占用计算机内存多少


2.1.7.3 程序优化

改进后的高效率的程序应该是这样的

#include 
#include 
using std::cout;
using std::endl;

int main()
{
	cout << "1. 网站404攻击" << endl;
	cout << "2. 网站篡改攻击" << endl;
	cout << "3. DNS攻击" << endl;
	cout << "4. 服务器重启攻击" << endl;
	cout << "5. 查看攻击记录" << endl;
	
	system("pause");
	return 0;
}

并且,经过单元测试后,没有问题。

不过对于初级的简单的项目来说,我们并不需要过多关注效率问题,而要关注于学习知识,因此,将上述代码中的

using std::cout;
using std::endl;

改为

using namespace std;

好吧到目前位置,你可能根本不知道我在说什么,没有关系,下面的小节中,我将会一点点详细讲给你!

2.1.8 程序的创建过程

本内容适合于初学者,介绍了程序创建的简易过程,内部细节暂时忽略,例如汇编过程等,并没有详细进行解释和展开,只需要了解即可。

2.1.9 头文件

我们先来放上代码,这样你就不需要再去前面翻阅了。

#include 
#include 
using namespace std;

int main()
{
	cout << "1. 网站404攻击" << endl;
	cout << "2. 网站篡改攻击" << endl;
	cout << "3. DNS攻击" << endl;
	cout << "4. 服务器重启攻击" << endl;
	cout << "5. 查看攻击记录" << endl;
	
	system("pause");
	return 0;
}

2.1.9.1 头文件的格式

最上面的两行就是头文件

#include 
#include 

头文件的书写有两种形式

原始形式:
#include <头文件名称>
#include "头文件名称"

  1. 我们先来区分一下**< >" "**
  • 形式一:使用英文状态在的一对儿尖括号(<、>)
#include 
  • 形式二:使用英文状态下的一对儿双引号("、")
#include "iostream"

思考:两种格式的区别是什么?

看此答案前,我建议你先看完后面讲解头文件的作用以及库文件的小节。

先来一张表格,再详细解释

格式 查找路径 结果 使用
< > 编译器默认目录 找不到相应头文件则编译失败 使用C++标准库
" " 程序当前目录 找不到就去默认目录找,再找不到则编译失败 使用用户自定义头文件

对于使用尖括号< >:代表预处理器在替换头文件部分时候,查找头文件代码的位置是直接从编译器的默认目录查找的。

并且要知道的是,编译器的默认目录,根据编译器、IDE等的不同,它的位置也都是不一样的,我们不需要关心它具体在哪里。

对于使用双引号" ":代表预处理器在替换头文件部分的时候,查找头文件代码的位置,先从当前程序目录下查找,找到了就替换;如果没有找到,则再从编译器默认目录下查找,如果还找不到,则会编译失败。


2.1.9.1.1 查看学习C++标准库代码

VS C++2010 Express中,可以直接看到编译器的默认目录,然后我们可以找到他们看一看。
编译器默认路径
我们找一下这个路径,这也就是C++标准库了
【C++学习之路】第二章——C++基础语法学习(1)之黑客攻击系统_第2张图片
这里的文件都可以使用VS C++2010打开,我们可以先尝试打开iostream看一下
【C++学习之路】第二章——C++基础语法学习(1)之黑客攻击系统_第3张图片
也许你看不懂这些是什么,没有关系,随着知识的深入,这些内容你会明白的,不过你至少要明白的是,iostream头文件里面也是一堆代码,是的目前来说,明白这些就足够了,在2.1.9.3小节中会用的到这个知识。

补充知识:std命名空间源代码的位置

File: "C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\new.h" 2 occurrences found on 2 lines
    52: namespace std {
    75: namespace std {
File: "C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\nothrow0.cpp" 1 occurrences found on 1 lines
    25: namespace std {
File: "C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\stddef.h" 1 occurrences found on 1 lines
    38: namespace std { typedef decltype(__nullptr) nullptr_t; }
File: "C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\yvals.h" 2 occurrences found on 2 lines
   554:   #define _STD_BEGIN	namespace std {
   566:    #define _C_STD_BEGIN	namespace std {	/* only if *.c compiled as C++ */
File: "C:\Program Files\Microsoft Visual Studio 10.0\VC\include\new.h" 2 occurrences found on 2 lines
    42: namespace std {
    64: namespace std {
File: "C:\Program Files\Microsoft Visual Studio 10.0\VC\include\stddef.h" 1 occurrences found on 1 lines
    38: namespace std { typedef decltype(__nullptr) nullptr_t; }
File: "C:\Program Files\Microsoft Visual Studio 10.0\VC\include\yvals.h" 2 occurrences found on 2 lines
   488:   #define _STD_BEGIN	namespace std {
   500:    #define _C_STD_BEGIN	namespace std {	/* only if *.c compiled as C++ */

  1. 头文件的名称约定
头文件类型 约定 实例 使用说明
C++旧式风格 扩展名为.h iostream.h C++98标准已经抛弃它
C旧式风格 扩展名为.h math.h C/C++中可以直接使用
C++新式风格 无扩展名 iostream C++中可以使用,需要引入namespace std使用
转换为C++新式风格的C 加上前缀c,无扩展名 cmath C++中可以使用,既可以按照C旧式风格直接使用,也可以引入namespace std使用

备注:此处参考了《C++ Primer Plus》中的表2.1

在C++风格的代码中,我们通常都会使用无扩展名的头文件,例如iostream和cmath,并且C++提倡引入命名空间(后面会解释这个),也就是C++风格的代码应该是这样的:

#include 	//而不是 iostream.h
#include 	//而不是 string.h
#include 	//而不是 math.h
#include 	//而不是 stdio.h
using namespace std;

值得一提的是,表格中的规则,我建议你掌握C++新风格的使用,至于旧风格,看到的时候能够认识即可

也就是,你只需要掌握这些

头文件类型 约定 实例 使用说明
C++新式风格 无扩展名 iostream C++中在namespace std中使用
转换为C++风格的C 无扩展名,前缀为c cstdio C++中直接使用或者在namespace std中使用

并且能够认识这些

头文件类型 约定 实例 使用说明
C旧式风格 以.h结尾 stdio.h C++中直接使用

不过也有一些特例,例如,Windows.h并没有cWindows这种写法,这涉及到Windows编程,先不用管它,你暂时只需要知道下面这两条即可。

#include //头文件
system("pause");	//暂停功能,也可以写成cin.get();

顺便说一句,在常用的头文件中,有几个头文件很令人迷惑, 需要重点解释一下,它们是

  • string
  • string.h
  • cstring

其中,string是C++新增的头文件,与后两者一点关系都没有,只是长得像,而string.h与cstring是一类头文件,分别是旧式风格与转换后的风格.

并且还要注意的是,string数据类型,只有在string头文件中才有,C语言是没有这种数据类型的。

下面用一个表格来描述。

头文件类型 头文件名称 能否使用string数据类型
C++新式风格 string 能,并且必须使用std空间
转换为C++后的C cstring 不能,因为C语言没有string类型
C旧式风格 string.h 不能,因为C语言没有string类型

以上说了这么多,是帮助你深入理解头文件的使用规则和头文件名称约定,下面将会给出,我们在编程的时候应该如何使用它们。

这一点非常重要,很多的书籍给出了很多的原理,但是不告诉我们实战的时候应该怎么写,应该用在哪里,最后让人一头雾水,难以快速掌握知识。

写法一
#include 
#include 
#include 
#include 
using namespace std;

int main()
{
	cout << "你好!" << endl;
	printf("你好!\n");
	string a; //后面会解释它的含义
	system("pause");
	return 0;
}
写法二
#include 
#include 
#include 
#include 
using std::cout;
using std::endl;
using std::string;
using std::printf;

int main()
{
	cout << "你好!" << endl;
	printf("你好!\n");
	string a; //后面会解释它的含义
	system("pause");
	return 0;
}
写法三
#include 
#include 
#include 
#include 

int main()
{
	std::cout << "你好!" << std::endl;
	std::printf("你好!\n");
	printf("你好!\n");//这个不加std也可以
	std::string a; //后面会解释它的含义
	system("pause");
	return 0;
}

好的目前你已经知道这些写法了,但是还会有疑惑,就是,头文件有啥什么用?namespace std是什么?后面的小节会讲解,请先接受这些内容,学习了后面的章节(头文件的作用和命名空间的使用)后,再回看以上内容。


思考:这几种写法的区别是什么?

我建议你看懂上面讲述的内容后,再看思考此问题。

我先抽象出三种写法核心内容,再为你逐步拆解讲解,请注意,下面的代码只是一部分,不是完整的程序。

  • 形式1:
#include 
using namespace std;
cout << "你好!" << endl;
  • 形式2:
#include 
using std::cout;
cout << "你好!" << std::endl;
  • 形式3
#include 
std::cout << "你好!" << std::endl;

对于形式1,使用了完整的命名空间std,这样后面的程序就可以直接使用std中的任意内容,而不需要加上 std:: 前缀。

但是如果你在接下来的代码中,只是使用了cout命令,并且使用了例如100个之多,显然没有必要使用全部的命名空间,这很浪费,因此,可以使用形式2中的形式,单独使用命名空间中的某一项指令。

如果你只使用了一个cout命令,形式2也是浪费的,因此可以使用形式3

用一张表格来概括一下:

头文件 命名空间使用情况 应用举例 适用场景 使用建议
iostream using namespace std; cout、endl 不关注效率时(初学者)、大量使用std空间内容时 不建议
iostream using std::cout cout、std::endl 大量使用cout,但很少使用endl时 建议
iostream 不使用 std::cout、std::endl cout和endl使用都很少时 建议

说明一下,我们后续会详细讲解命名空间,之所以不建议使用第一种,是为了防止引发不必要的重名冲突,先了解一下即可,后续会深入讲解。
附上一个拓展学习链接:std命名空间


2.1.9.2 头文件的作用

关于头文件的格式,大家应该很清楚了,不过应该也有一些不太清楚的地方,下面我来讲解头文件的作用,然后,请你回看上一小节,并结合本节内容,重新进行理解。

如下程序是正确的,能够编译成功

#include 

int main()
{
	std::cout << "你好!" << std::endl;
	return 0;
}

而如下程序,缺少了头文件,不能编译成功

int main()
{
	std::cout << "你好!" << std::endl;
	return 0;
}

启动VS C++2010编译后,会报错
VS编译器的报错
此时,我们称编译器不能识别cout和endl,不能识别的原因:

  • 直接原因:没有添加iostream头文件
  • 根本原因:编译器不能识别cout和endl指令

思考:为什么加上头文件,编译器就能够识别这两个指令了?

很明显,就是因为加上了头文件iostream。


因此,头文件的作用,就是帮助编译器识别相应的指令。

但是这样描述并不专业,从专业的角度来说,头文件iostream本身能够实现cout指令,在程序编译过程中,预处理器将iostream文件包含了进来,因此程序可以使用cout,请看下一小节后,再来回看这一段内容,这样你就能够理解了。

2.1.9.3 头文件的预处理过程

对于程序员写好的代码,执行编译之后,会在编译过程进行之前,编译器会调用预处理器,进行预处理过程,请看下面的流程图。

执行编译
头文件部分被替换为头文件代码
源代码
编译器调用预处理器
执行编译过程

也就是说,预处理器将下面这段内容

#include 

替换为了iostream文件中的代码,也就是这些:
【C++学习之路】第二章——C++基础语法学习(1)之黑客攻击系统_第4张图片
这时候,我们称预处理器将头文件内容包含进了该程序中。

还记得2.1.9.1.1小节学到的内容吗,如果忘记了请你回看一下,然后再来理解这一小节。

不过请注意,这种替换并不改变源代码文件,是一种隐性替换,不是真正的替换掉了,这种说法只是为了方便你理解这件事。

当然,你也不妨理解为添加头文件,就相当于添加了头文件中的代码,头文件只是一个“代号”。因此,该源程序文件能够使用头文件中的功能

2.1.9.4 头文件的使用

不管是单文件还是多文件,头文件一定要加在最前面,因为编译的顺序以及程序的执行顺序都是自上而下的,如果写成下面这样

std::cout << "你好!";
#include 

这样的程序是不能执行的,一般情况下都是后面的内容可以调用前面的内容(不是绝对的,学到后面你会明白),因此来说:头文件一定要写在最前面,不过后面学习到多文件的时候,你会遇到这样的命令

#pragma once

我先告诉你这代表该文件只能被编译一次,头文件需要在它的下面写。

2.1.9.5 头文件在项目中的使用方法

我先给出你目前水平的简单使用方法,再给出你专业的方法,专业的方法,我建议你学会了多文件的使用后再来回看。

  • 简单的方法
  1. 先写出必要的头文件,例如iostream和Windows.h,以确保你能够完成最基础的事情,例如下面这样
#include 
#include 
using namespace std;

int main()
{
	cout << "你好!" << endl;
	system("pause");
	return 0;
}

这是基础款,必须要有的头文件。

  1. 在程序的编辑过程中,如果某些指令需要新的头文件支持,则再将头文件添加到最前面,比如需要string类型,则在最前面加上一个头文件
#include 
#include 
#include 	// ② 新添加的
using namespace std;

int main()
{
	cout << "你好!" << endl;
	string str;// ① 我们需要这条语句,因此加上头文件
	system("pause");
	return 0;
}
  • 专业的方法
    1. 根据设计好的架构,建立好多个头文件和源程序文件,每一个源程序文件代表一个功能模块,其中一个是包含main函数的文件
    2. 然后设计好头文件,注意加上#pragma once
    3. 每一个源程序文件,都要遵循上面说的简单的方法来添加头文件
    4. 先设计好模块文件,最后编辑包含main函数的文件,它会调用刚才编辑好的其他模块文件。

对于专业的方法,请掌握多文件之后,再来回看。

你可能感兴趣的:(C++,#,C++学习之路)