遵循原则:从实战中学习知识
法律声明:本系统仅能攻击自己搭建的网站,作为学习使用,不会触犯法律
time:2020.01.20
author:姜海天
IDE:VS C++2010 Express
大纲是最重要的,一定要仔细阅读,不可忽略!
本子项目涉及到的知识点如下
设计一个功能菜单,实现黑客攻击系统基本信息的输出
要求的必要信息: 实现网站404攻击,网站篡改攻击,DNS攻击,服务器重启攻击,且记录下来攻击记录
整体架构 |
---|
设计一个能够输出信息的菜单 |
ADT模型1:输出需求中所要求的信息 |
---|
1. 网站404攻击 |
2. 网站篡改攻击 |
3. DNS攻击 |
4. 服务器重启攻击 |
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;
}
对于初学者并不知道我在干什么,没有关系,请先接受这些内容,后面的小节会详细讲解这些内容。
这里只有一个ADT模型的实现,并且由于是初学,所以只是借用了软件工程中单元测试的名字,并不是标准的单元测试,只是单纯的验证程序结果的正确性
显然,程序运行结果是正确的,并且是符合要求的。
显然,程序的结果是正确的
对于此程序,效率是不够高的,并且有一些冗杂的没有必要的代码。
思考:什么是高效率的程序?
在计算机工程中,需要综合考量时间复杂度和空间复杂度。
时间复杂度:程序耗时多少
空间复杂度:占用计算机内存多少
改进后的高效率的程序应该是这样的
#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;
好吧到目前位置,你可能根本不知道我在说什么,没有关系,下面的小节中,我将会一点点详细讲给你!
本内容适合于初学者,介绍了程序创建的简易过程,内部细节暂时忽略,例如汇编过程等,并没有详细进行解释和展开,只需要了解即可。
我们先来放上代码,这样你就不需要再去前面翻阅了。
#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;
}
最上面的两行就是头文件
#include
#include
头文件的书写有两种形式
原始形式:
#include <头文件名称>
#include "头文件名称"
#include
#include "iostream"
思考:两种格式的区别是什么?
看此答案前,我建议你先看完后面讲解头文件的作用以及库文件的小节。
先来一张表格,再详细解释
格式 | 查找路径 | 结果 | 使用 |
---|---|---|---|
< > | 编译器默认目录 | 找不到相应头文件则编译失败 | 使用C++标准库 |
" " | 程序当前目录 | 找不到就去默认目录找,再找不到则编译失败 | 使用用户自定义头文件 |
对于使用尖括号< >:代表预处理器在替换头文件部分时候,查找头文件代码的位置是直接从编译器的默认目录查找的。
并且要知道的是,编译器的默认目录,根据编译器、IDE等的不同,它的位置也都是不一样的,我们不需要关心它具体在哪里。
对于使用双引号" ":代表预处理器在替换头文件部分的时候,查找头文件代码的位置,先从当前程序目录下查找,找到了就替换;如果没有找到,则再从编译器默认目录下查找,如果还找不到,则会编译失败。
VS C++2010 Express中,可以直接看到编译器的默认目录,然后我们可以找到他们看一看。
我们找一下这个路径,这也就是C++标准库了
这里的文件都可以使用VS C++2010打开,我们可以先尝试打开iostream看一下
也许你看不懂这些是什么,没有关系,随着知识的深入,这些内容你会明白的,不过你至少要明白的是,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++ */
头文件类型 | 约定 | 实例 | 使用说明 |
---|---|---|---|
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是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是什么?后面的小节会讲解,请先接受这些内容,学习了后面的章节(头文件的作用和命名空间的使用)后,再回看以上内容。
思考:这几种写法的区别是什么?
我建议你看懂上面讲述的内容后,再看思考此问题。
我先抽象出三种写法核心内容,再为你逐步拆解讲解,请注意,下面的代码只是一部分,不是完整的程序。
#include
using namespace std;
cout << "你好!" << endl;
#include
using std::cout;
cout << "你好!" << std::endl;
#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命名空间
关于头文件的格式,大家应该很清楚了,不过应该也有一些不太清楚的地方,下面我来讲解头文件的作用,然后,请你回看上一小节,并结合本节内容,重新进行理解。
如下程序是正确的,能够编译成功
#include
int main()
{
std::cout << "你好!" << std::endl;
return 0;
}
而如下程序,缺少了头文件,不能编译成功
int main()
{
std::cout << "你好!" << std::endl;
return 0;
}
启动VS C++2010编译后,会报错
此时,我们称编译器不能识别cout和endl,不能识别的原因:
思考:为什么加上头文件,编译器就能够识别这两个指令了?
很明显,就是因为加上了头文件iostream。
因此,头文件的作用,就是帮助编译器识别相应的指令。
但是这样描述并不专业,从专业的角度来说,头文件iostream本身能够实现cout指令,在程序编译过程中,预处理器将iostream文件包含了进来,因此程序可以使用cout,请看下一小节后,再来回看这一段内容,这样你就能够理解了。
对于程序员写好的代码,执行编译之后,会在编译过程进行之前,编译器会调用预处理器,进行预处理过程,请看下面的流程图。
也就是说,预处理器将下面这段内容
#include
替换为了iostream文件中的代码,也就是这些:
这时候,我们称预处理器将头文件内容包含进了该程序中。
还记得2.1.9.1.1小节学到的内容吗,如果忘记了请你回看一下,然后再来理解这一小节。
不过请注意,这种替换并不改变源代码文件,是一种隐性替换,不是真正的替换掉了,这种说法只是为了方便你理解这件事。
当然,你也不妨理解为添加头文件,就相当于添加了头文件中的代码,头文件只是一个“代号”。因此,该源程序文件能够使用头文件中的功能。
不管是单文件还是多文件,头文件一定要加在最前面,因为编译的顺序以及程序的执行顺序都是自上而下的,如果写成下面这样
std::cout << "你好!";
#include
这样的程序是不能执行的,一般情况下都是后面的内容可以调用前面的内容(不是绝对的,学到后面你会明白),因此来说:头文件一定要写在最前面,不过后面学习到多文件的时候,你会遇到这样的命令
#pragma once
我先告诉你这代表该文件只能被编译一次,头文件需要在它的下面写。
我先给出你目前水平的简单使用方法,再给出你专业的方法,专业的方法,我建议你学会了多文件的使用后再来回看。
#include
#include
using namespace std;
int main()
{
cout << "你好!" << endl;
system("pause");
return 0;
}
这是基础款,必须要有的头文件。
#include
#include
#include // ② 新添加的
using namespace std;
int main()
{
cout << "你好!" << endl;
string str;// ① 我们需要这条语句,因此加上头文件
system("pause");
return 0;
}
对于专业的方法,请掌握多文件之后,再来回看。