definition
Affine Transformation
Linear Transformation + Translate
线性变换 (Linear Transformation): 你可以对这个网格进行 旋转 (Rotate)、缩放 (Scale)、切变 (Shear)。
如果可以邀请世界上任何一个人,你希望谁作为晚餐的客人?
你想出名吗?以什么方式?
在打电话之前,你会排练要说的内容吗?为什么?
对你来说,怎样的一天才算“完美”?
你上次唱歌给自己听是什么时候?唱歌给别人听呢?
如果你能活到90岁,并在生命的最后60年里保持30岁的头脑或身体,你会选择哪一个?
你是否有预感自己会怎么死?
说出你和你的配对者似乎有的三个共同点。
你对生活中最感激的事情是什么?
如果你可以改变你成长过程中的任何一点,你会改变什么?
用四分钟时间尽可能详细地告诉你的配对者你的生活故事。
如果你明天醒来说可以拥有任何一种特质或能力,那会是什么?
如果水晶球可以告诉你关于你自己、你的生活、未来或是其他任何事情的真相,你最想知道什么?
是否有什么事情你梦想已久,一直没有去做?为什么没有去做?
你一生中最大的成就是什么?
你在友谊中最看重什么?
你最珍贵的记忆是什么?
你最糟糕的记忆是什么?
如果你知道一年后你将会突然死去,你会改变现在的生活方式吗?为什么?
友谊对你意味着什么?
爱和感情在你的生活中扮演什么角色?
交替分享你认为对方的正面特质。总共分享五项。
你的家庭有多亲密和温暖?你觉得自己童年比大多数人快乐吗?
你对与母亲的关系感觉如何?
各自说出三个真实的“我们”。例如:“我们都在这个房间里感觉……”。
完成句子:“我希望我有一个可以分享……的人”
如果你要给你的室友或者朋友写信,分享他们需要知道的重要事情。
告诉你的室友或者朋友你感激他们什么;这次要非常诚实,说出你可能不会对刚认识的人说的话。
鼓励你的室友分享生活中一个尴尬的时刻。
你上次在别人面前哭是什么时候?自己一个人哭呢?
告诉你的室友或者朋友你已经喜欢他们什么。
有什么事是你认为不能开玩笑的?
如果你今晚就要去世,没有机会与任何人沟通,你最遗憾没有告诉某人的事情是什么?为什么还没有告诉他们?
你的房子着火了,里面所有你拥有的东西都在里面。在救出你所爱的人和宠物之后,你有时间安全地冲回去再救一样东西。那会是什么?为什么?
在你的家人中,谁的去世会让你感到最痛苦?为什么?
分别问另一个人问题,询问你希望他们告诉你一些事情,看看他们会如何回答。另外,请你的室友反馈出你对所选问题的感觉。
site:可以限制你搜索范围的域名.
site:.gov
inurl:用于搜索网页上包含的URL,这个语法对寻找网页上的搜索,帮助之类的很有用.
inurl:qq.com
intext: 只搜索网页
部分中包含的文字(也就是忽略了标题、URL等的文字)intitle: 查包含关键词的页面,一般用于社工别人的webshell密码
intitle:edu
filetype:搜索文件的后缀或者扩展名
filetype:txt
filetype:pdf
link: 可以得到一个所有包含了某个指定URL的页面列表.
link:weixin.com
related: 搜索相关网站
related:qq.com
define: 搜索词语的解释, 可以搜索名词及名人
define:锻炼
天天:define
刘翔 define
吴亦凡 define
翻译
I don’t know the means of the word 翻译
翻译 apple
allinurl: 搜索网址中包含以下所有词
allinurl:渗透 安全
allintext: 指定范围搜索(正文出现关键词)
allintext:渗透
精确搜索: 给关键词加引号
“渗透”
‘渗透’
- (减号): 从搜索结果中排除特定字词,
从要排除的字词前加上’-‘
linux常用命令
#会显示所有搜索结果
linux常用命令 -CSDN
#会屏蔽掉搜索结果中的CSDN相关内容
+ (加号): 只显示+后面的内容
linux常用命令 +CSDN
#只显示搜索结果中CSDN的相关内容
linux常用命令 +博客园
#只显示搜索结果中博客园的相关内容
cache: 查看网站的Google缓存版本,
在相对应网址前加上”cache:”
cache:qq.com
搜索#标签: 在字词前加上’#’
#body
#安全
搜索特定价格: 在数字前加上$
$400
搜索社交媒体: 在用于搜索社交媒体的字词前加上 @
@qq
@twitter
camera: 在某个数字范围内执行搜索
在两个数字之间加上…
camera $50…$100
camera 9999…100000
inanchor or allinanchor: 搜索范围限制在页面的链接锚点描述文本进行搜索
AROUND: 查找两个字或词在不超过指定的距离
渗透 AROUND(5) 安全
OR: 组合搜索.默认搜索,中间空格是与(AND),而使用OR,可以达到或的效果。
在各个搜索查询之间加上“OR”
marathon OR race
渗透 OR 安全
|代表或: login|admin|manget
不常用语法:
Phonebook: 搜索电话列表
Rphonebook: 搜索住宅电话列表
Bphonebook: 商业电话列表
Author: 搜索Google中新闻组帖子的作者
Group: 搜索Google标题
Inanchor: 在链接文本中查找文本
Masgid: 通过消息id来查找谷歌的帖子
Daterange: 查找某个特定日期范围内发布的网页
Insubject: 搜索Googlegroup的主题行
Stocks: 搜索股票信息
Info: 显示Google的摘要信息
Define: 显示某术语的定义
Numrang: 搜索数字需要两个参数一个最小数,一个最大数,用破折号隔开
~ 同意词即类似的词
. 单一的通配符
* 通配符,可代表多个字母
“ ” 精确查询匹配
布尔操作:
and 与
or 或
not 不
叠加使用: 组合使用上述所有方法,自行测试
注意事项:
1、所有的冒号都是半角,也就是英文的冒号,而不是中文的冒号
2、空格很重要,关键词之间一定要加空格
Google Hacking数据库: 汇集了非常多的有价值的搜索语句
https://www.exploit-db.com/google-hacking-database
查找后台地址:
site:xxx.com intext:管理|后台|登录|登陆|用户名|密码|系统|账号|login|system|admin
site:xxx.com inurl:login|admin|manage|member|admin_login|login_admin|system|login|user|main|cms
inurl:edu.cn intitle:管理
site:xxx.com inurl:login|inurl:admin|inurl:admin_login|inurl:system
site:xxx.com intitle:管理|后台|后台管理|登录|登陆
inurl:login|admin|admin_login|login_admin|system|user
site:xxx.com
查找文本内容:
site:xxx.com intext:管理|后台|登陆|用户名|密码|验证码|系统|帐号|admin|login|sys|managetem|password|username
查找可注入点:site:xxx.com inurl:aspx|jsp|php|asp
site:xxx.com inurl:php?id=
查找上传漏洞:site:xxx.com inurl:file|load|editor|files|
找eweb编辑器:site:xxx.com inurl:ewebeditor|editor|uploadfile|eweb|edit
存在的数据库:site:域名 filetype:mdb|asp|#
site:xxx.com filetype:mdb
site:xxx.com filetype:数据库格式
查看脚本类型:site:xxx.com filetype:asp/aspx/php/jsp
site:xxx.com filetype:php
查找目录遍历漏洞: site:xxx.com intitle:index of
社工信息: site:xxx.com intitle:账号|密码|工号|学号|身份证
搜索各类开源的网站上面的信息: site:github.com intext:xiaodi8.com
迂回策略入侵:inurl:cms/data/templates/images/index/
实战演示
01 首先用google搜索这个站点的基本情况
site:xxx.com
从搜索结果中找到了几处该站点的域名
1 | http://aaa.xxx.com |
然后查看这几个域名的ip,并确认是否存在CDN
发现不存在CDN服务,并且子域名ip有的也不同
02 搜索该站点的后台地址
site:xxx.com intext:管理
site:xxx.com inurl:login
site:xxx.com inurl:admin
site:xxx.com intitle:管理
等其他方式也可以,自行组合即可
最终获取到了多个后台地址
03 查看服务器脚本语言
site:aaa.xxx.com filetype:asp
site:aaa.xxx.com filetype:php
site:aaa.xxx.com filetype:aspx
site:aaa.xxx.com filetype:jsp
等其他方式也可判断
最终获取到了基本的搭建组合后
04 尝试获取漏洞
site:aaa.xxx.com intext:ftp://.
site:bbb.xxx.com inurl:file
site:ccc.xxx.com inurl:load
等其他漏洞的关键字自行搜索
得到地址后就可以进行下一步渗透了
05 获取人员类相关信息
获取二级域名
site:xxx.com
获取邮箱地址
site:xxx.com intext:*@xxx.com
获取电话信息
site:xxx.com intext:电话
在搜集到信息后,可以生成社工字典,使用工具进行跑一遍
for 适合在预先知道迭代次数时使用
1 | for (int i = 1; i <= n; i++) { |
此求和函数的操作数量与输入数据大小 成正比,或者说成“线性关系”
while 循环比 for 循环的自由度更高。在 while 循环中,我们可以自由地设计条件变量的初始化和更新步骤。
for(for( ))
每一次嵌套都是一次“升维”,将会使时间复杂度提高至“立方关系”“四次方关系”,以此类推。
递归(recursion)是一种算法策略,通过函数调用自身来解决问题。它主要包含两个阶段。
虽然从计算角度看,迭代与递归可以得到相同的结果,但它们代表了两种完全不同的思考和解决问题的范式。
有趣的是,如果函数在返回前的最后一步才进行递归调用,则该函数可以被编译器或解释器优化,使其在空间效率上与迭代相当。这种情况被称为尾递归(tail recursion)。
| 迭代 | 递归 | |
|---|---|---|
| 实现方式 | 循环结构 | 函数调用自身 |
| 时间效率 | 效率通常较高,无函数调用开销 | 每次函数调用都会产生开销 |
| 内存使用 | 通常使用固定大小的内存空间 | 累积函数调用可能使用大量的栈帧空间 |
| 适用问题 | 适用于简单循环任务,代码直观、可读性好 | 适用于子问题分解,如树、图、分治、回溯等,代码结构简洁、清晰 |
1 | #include <stdio.h> |
1 | #include <stdio.h> |
编写一个程序,提示用户输入大写字母。使用嵌套循环以下面金字塔型的格式打印字母:
A
ABA
ABCBA
ABCDCBA
ABCDEDCBA
打印这样的图形,要根据用户输入的字母来决定。例如,上面的图形是在用户输入E后的打印结果。
1 | #include <stdio.h> |
考虑下面两个无限序列:
1.0 + 1.0/2.0 + 1.0/3.0 + 1.0/4.0 + …
1.0 - 1.0/2.0 + 1.0/3.0 - 1.0/4.0 + …
编写一个程序计算这两个无限序列的总和,直到到达某次数。提示:奇数个-1 相乘得-1,偶数个-1相乘得1。让用户交互地输入指定的次数,当用户输入0或负值时结束输入。查看运行100项、1000项、10000项后的总和,是否发现每个序列都收敛于某值?
1 | #include <stdio.h> |
1 | #include <stdio.h> |
1 | #include <stdio.h> |
1 | // wordcnt.c -- 统计字符数、单词数、行数 |
1 | #include <stdio.h> |
续150美元为20%
余下的为25%
让程序可以给出一个供选择的工资等级菜单。使用switch完成工资等级选择。运行程序后,显示的菜单应该类似这样:1
2
3
4
5
6*****************************************************************
Enter the number corresponding to the desired pay rate or action:
1) $8.75/hr 2) $9.33/hr
2) $10.00/hr 4) $11.20/hr
3) quit
*****************************************************************
如果选择 1~4 其中的一个数字,程序应该询问用户工作的小时数。程序要通过循环运行,除非用户输入 5。如果输入 1~5 以外的数字,程序应提醒用户输入正确的选项,然后再重复显示菜单提示用户输入。使用#define创建符号常量表示各工资等级和税率。
1 | #include <stdio.h> |
switch语句中使用的表达式必须具有整数类型或枚举类型,或者是具有单个转换函数到整数或枚举类型的类类型,无法进行bool等逻辑运算。编写一个程序,在循环中用 switch 语句实现用户输入不同的字母时有不同的响应,即输入a的响应是让用户输入洋蓟的磅数,b 是甜菜的磅数,c 是胡萝人的磅数,q 是退出订购。程序要记录累计的重量。即,如果用户输入 4磅的甜菜,然后输入 5磅的甜菜,程序应报告9 磅的甜菜。然后,该程序要计算货物总价、折扣(如果有的话) 、运费和包装费。随后,程序应显示所有的购买信息: 物品售价、订购的重量(单位:磅) 、订购的蔬菜费用、订单的总费用、折扣(如果有的话)、运费和包装费,以及所有的费用总额。
1 | #include <stdio.h> |
使用指针1
2
3
4
5
6
7void alter(int * pa, int * pb)
{
int temp;
temp = *pa + *pb;
*pb = *pa - *pb;
*pa = temp;
}
or 1
2
3
4
5void alter(int * pa, int * pb)
{
*pa += *pb;
*pb = *pa - 2 * *pb;
}
1 | #include <stdio.h> |
编写一个函数,返回储存在double类型数组中最大值的下标,并在一个简单的程序中测试该函数。
1 | #include <stdio.h> |
编写一个函数,把double类型数组中的数据倒序排列,并在一个简单的程序中测试该函数。
1 | #include <stdio.h> |
使用编程练习2中的拷贝函数,把一个内含7个元素的数组中第3~第5个元素拷贝至内含3个元素的数组中。该函数本身不需要修改,只需要选择合适的实际参数(实际参数不需要是数组名和数组大小,只需要是数组元素的地址和待处理元素的个数)。
1 | #include <stdio.h> |
编写一个程序,初始化一个double类型的3×5二维数组,使用一个处理变长数组的函数将其拷贝至另一个二维数组中。还要编写一个以变长数组为形参的函数以显示两个数组的内容。这两个函数应该能处理任意N×M数组。
1 | #include <stdio.h> |
编写一个函数,把两个数组中相对应的元素相加,然后把结果储存到第 3 个数组中。
1 | #include <stdio.h> |
编写一个程序,提示用户输入3组数,每组数包含5个double类型的数(假设用户都正确地响应,不会输入非数值数据)。该程序应完成下列任
务。
a.把用户输入的数据储存在3×5的数组中
b.计算每组(5个)数据的平均值
c.计算所有数据的平均值
d.找出这15个数据中的最大值
e.打印结果
每个任务都要用单独的函数来完成(使用变长数组作为函数形参的方式)。完成任务b,要编写一个计算并返回一维数组平均值的函数,利用循环调用该函数3次。对于处理其他任务的函数,应该把整个数组作为参数,完成任务c和d的函数应把结果返回主调函数。
1 | #include <stdio.h> |
1 | #include <stdio.h> |
1 | #include <stdio.h> |
1 | #include <stdio.h> |
How many words do you wish to enter? 5
Enter 5 words now:
I enjoyed doing this exerise
Here are your words:
I
enjoyed
doing
this
exercise
1 | #include <stdio.h> |
假设在测试程序时要暂时跳过一块代码,如何在不移除这块代码的前提下完成这项任务?
使用条件编译指令。一种方法是使用#ifndef:1
2
3
4
5
6#define _SKIP_ /* 如果不需要跳过代码,则删除这条指令 */
#ifndef _SKIP_
/* 需要跳过的代码 */
#endif
有一个不常用的命名约定,即在名称前带c或k前缀来表示常量(如,c_level或k_line)。
在C程序中,既可以使用和显示不同进制的数。不同的进制要使用不同的转换说明。以十进制显示数字,使用%d;以八进制显示数字,使用%o;以十六进制显示数字,使用%x。另外,要显示各进制数的前缀0、0x和0X必须分别使用%#o、%#x、%#X。,
打印unsigned int类型的值,使用%u转换说明;打印long类型的值,使用%ld转换说明。如果系统中int和long的大小相同,使用%d就行。但是,这样的程序被移植到其他系统(int和long类型的大小不同)中会无法正常工作。在x和o前面可以使用l前缀,%lx表示以十六进制格式打印long类型整数,%lo表示以八进制格式打印long类型整数。
C语言有多种printf()格式。对于short类型,可以使用h前缀。%hd表示以十进制显示short类型的整数,%ho表示以八进制显示short类型的整数。h和l前缀都可以和u一起使用,用于表示无符号类型。例如,%lu表示打印unsigned long类型的值。
scanf()的输入形式,scanf("%d",&Alphabet) 要先将%d包含以后才能使用&进行传参。
十六进制浮点数printf("And it's %a in hexadecimal, powers of 2 notation\n",a boat);
转换标准.png)
的修饰符.png)
注意类型可移植性
中的标记.png)
转换说明.png)
转换说明中的修饰符.png)
转换说明中的修饰符续.png)
scanf() 更像是获取单词的函数,而不是获取整个字符串的函数。%s 转换说明时,scanf() 会从第一个非空白字符开始,读取直到遇到下一个空白字符(空格、制表符、换行符等)为止。%10s,scanf() 将读取指定数量的字符或者在遇到第一个空白字符时停止(以先满足条件为准)。scanf()的典型用法是读取并转换混合数据类型为某种标准形式。
例如,如果输入行包含一种工具名、库存量和单价,就可以使用scanf()。
从数值方面而不是从真/假方面来看测试条件。要牢记:关系表达式为真,求值得1;关系表达式为假,求值得0。因此,这些表达式实际上相当于数值。
例如,用while(goats)替换while (goats !=0),因为表达式goats != 0和goats都只有在goats的值为0时才为0或假。
第1种形式(while (goats != 0))对初学者而言可能比较清楚,但是第2种形式(while (goats))才是C程序员最常用的。
如果待比较的一个值是常量,可以把该常量放在左侧有助于编译器捕获错误:
5 = canoes <—— 语法错误
5 == canoes <—— 检查canoes的值是否为5
可以这样做是因为C语言不允许给常量赋值,编译器会把赋值运算符的这种用法作为语法错误标记出来。许多经验丰富的程序员在构建比较是否相等的表达式时,都习惯把常量放在左侧。



ctype.h系列的字符函数(如,issapce()和isalpha())为创建以分类字符为基础的测试表达式提供了便捷的工具。
?:expression1 ? expression2 : expression3
如果 expression1 为真(非 0),那么整个条件表达式的值与 expression2的值相同;如果expression1为假(0),那么整个条件表达式的值与expression3的值相同。
1 | (5 > 3) ? 1 : 2 值为1 |
switch在圆括号中的测试表达式的值应该是一个整数值(包括char类型)。case标签必须是整数类型(包括char类型)的常量或整型常量表达式(即,表达式中只包含整型常量)。不能用变量作为case标签。
switch的构造如下:1
2
3
4
5
6
7
8
9switch ( 整型表达式)
{
case 常量1:
语句 <--可选
case 常量2:
语句 <--可选
default : <--可选
语句 <--可选
}
考虑下面的输入:is 28 12.4
在我们眼中,这就像是一个由字符、整数和浮点数组成的字符串。但是对 C程序而言,这是一个字节流。第1个字节是字母i的字符编码,第2个字节是字母s的字符编码,第3个字节是空格字符的字符编码,第4个字节是数字2的字符编码,等等。所以,如果get_long()函数处理这一行输入,第1个字符是非数字,那么整行输入都会被丢弃,包括其中的数字,因为这些数字只是该输入行中的其他字符:1
2while ((ch = getchar()) != '\n')
putchar(ch); // 处理错误的输入
虽然输入流由字符组成,但是也可以设置scanf()函数把它们转换成数值。例如,考虑下面的输入:42
如果在scanf()函数中使用%c转换说明,它只会读取字符4并将其储存在char类型的变量中。如果使用%s转换说明,它会读取字符4和字符2这两个字符,并将其储存在字符数组中。如果使用%d转换说明,scanf()同样会读取两个字符,但是随后会计算出它们对应的整数值:4×10+2,即42,然后将表示该整数的二进制数储存在 int 类型的变量中。如果使用%f 转换说明,scanf()也会读取两个字符,计算出它们对应的数值42.0,用内部的浮点表示法表示该值,并将结果储存在float类型的变量中。
不能像普通变量声明那样使用同一类型的变量列表:1
2void dibs(int x, y, z) /* 无效的函数头 */
void dubs(int x, int y, int z) /* 有效的函数头 */
如果变量是同一类型,这种形式可以用逗号分隔变量名列表,如下所示:1
2void dibs(x, y, z)
int x, y, z; /* 有效 */
在使用函数之前,要用ANSI C形式声明函数原型:void show_n_char(char ch, int num);
当函数接受参数时,函数原型用逗号分隔的列表指明参数的数量和类型。根据个人喜好,你也可以省略变量名:void show_n_char(char, int);
在原型中使用变量名并没有实际创建变量,char仅代表了一个char类型的变量,以此类推。
1 | // 下面这行代码既是函数定义,也是函数原型 |
递归的关键在于每个递归调用都会等待它的下一级递归完成,然后再继续执行后面的代码,最终实现整个递归的效果。
可以假设有一条函数调用链——fun1()调用fun2()、fun2()调用 fun3()、fun3()调用fun4()。当 fun4()结束时,控制传回
fun3();当fun3()结束时,控制传回 fun2();当fun2()结束时,控制传回fun1()。递归的情况与此类似,只不过fun1()、fun2()、fun3()和fun4()都是相同的函数。

返回值不仅可以赋给变量,也可以被用作表达式的一部分。
返回值不一定是变量的值,也可以是任意表达式的值。return (n < m) ? n : m;
void variables(double *, double *, double *);
如果初始化数组时省略方括号中的数字,编译器会根据初始化列表中的项数来确定数组的大小。
整个数组的大小除以单个元素的大小就是数组元素的个数。
for (index = 0; index < sizeof days / sizeof days[0]; index++)
sizeof days是整个数组的大小(以字节为单位),sizeof day[0]是数组中一个元素的大小(以字节为单位)。
只有在函数原型或函数定义头中,才可以用int ar[]代替int * ar
由于函数原型可以省略参数名,所以下面4种原型都是等价的:1
2
3
4int sum(int *ar, int n);
int sum(int *, int);
int sum(int ar[], int n);
int sum(int [], int);
但是,在函数定义中不能省略参数名。下面两种形式的函数定义等价:1
2
3
4
5
6
7
8
9int sum(int *ar, int n)
{
}
int sum(int ar[], int n)
{
}
如果函数的意图不是修改数组中的数据内容,那么在函数原型和函数定义中声明形式参数时应使用关键字const。1
2
3
4
5
6
7
8
9int sum(const int ar[], int n); /* 函数原型 */
int sum(const int ar[], int n) /* 函数定义 */
{
int i;
int total = 0;
for( i = 0; i < n; i++)
total += ar[i];
return total;
}
一般而言,如果编写的函数需要修改数组,在声明数组形参时则不使用const;如果编写的函数不用修改数组,那么在声明数组形参时最好使用const。
复合字面量
普通的数组声明:int diva[2] = {10, 20};
下面的复合字面量创建了一个和diva数组相同的匿名数组,也有两个int类型的值:(int [2]){10, 20} // 复合字面量
(int []){50, 20, 90} // 内含3个元素的复合字面量1 | int sum(const int ar[], int n); |
可以把这种用法应用于二维数组或多维数组。例如,下面的代码演示了如何创建二维int数组并储存其地址:1
2
3int (*pt2)[4]; // 声明一个指向二维数组的指针,该数组内含2个数组元素,
// 每个元素是内含4个int类型值的数组
pt2 = (int [2][4]) { {1,2,3,-9}, {4,5,6,-8} };
如上所示,该复合字面量的类型是int [2][4],即一个2×4的int数组。
变长数组
C99引入了变长数组(Variable-Length Arrays,VLA),允许使用变量表示数组的维度。以下是一个使用变长数组的例子:1
2
3int quarters = 4;
int regions = 5;
double sales[regions][quarters]; // 变长数组(VLA)
变长数组的限制和特点:
存储类别限制: 变长数组必须是自动存储类别,不能使用static或extern存储类别说明符。
初始化限制: 不能在声明中初始化变长数组。
可选特性: C11标准将变长数组作为可选特性,而不是必须强制实现的特性。
不能改变大小: 变长数组的”变”指的是在创建数组时可以使用变量指定数组的维度,而不是可以修改已创建数组的大小。一旦创建,数组的大小保持不变。
计算二维数组元素之和的示例:
考虑一个函数 sum2d,计算int类型的二维数组所有元素之和。以下是该函数的声明和定义:1
2
3
4
5
6
7
8
9
10
11
12
13
14int sum2d(int rows, int cols, int ar[rows][cols]); // ar是一个变长数组(VLA)
int sum2d(int rows, int cols, int ar[rows][cols])
{
int r;
int c;
int tot = 0;
for (r = 0; r < rows; r++)
for (c = 0; c < cols; c++)
tot += ar[r][c];
return tot;
}
ar 是一个二维变长数组,使用了 rows 和 cols 作为两个维度。rows 和 cols 来表示二维数组的大小,可以处理任意大小的二维int数组。ar 之前先声明 rows 和 cols。此外,函数的定义也可以使用省略形参名的方式:int sum2d(int, int, int ar[*][*]); // ar是一个变长数组(VLA),省略了维度形参名
声明指针
声明指针时需要需要带有变量,如果不想加变量则需要有*
不要混淆 *(dates+2) 和*dates+2 。间接运算符()的优先级高于+,所以 `dates+2` 相当于(*dates)+2:1
2
3*(dates + 2) // dates第3个元素的值
*dates + 2 // dates第1个元素的值加2
一元运算符*和++的优先级相同,但结合律是从右往左,所以start++先求值,然后才是*start。也就是说,指针start先递增后指向。使用后缀形式(即start++而不是++start)如果使用*++start,顺序则反过来,先递增指针,再使用指针指向位置上的值。如果使(*start)++,则先使用start指向的值,再递增该值,而不是递增指针。这样,指针将一直指向同一个位置,但是该位置上的值发生了变化。虽然*start++的写法比较常用,但是*(start++)这样写更清楚。
当涉及到 const 修饰符和指针时,有几个要点需要注意:
const修饰指针所指向的数据,表示指针所指向的数据不能通过这个指针进行修改。例如:1 | const double *pd = rates; // pd指向数组的首元素 |
const 的指针 (const double *pc) 可以指向非 const 的数据,但不能通过这个指针修改所指向的数据:1 | double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5}; |
const 的指针(double *pnc)可以指向 const 数据,但也不能通过这个指针修改所指向的 const 数据:1 | double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5}; |
const 可以用于指向数组的指针,保护数组数据不被修改:1 | double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5}; |
const 还可以用于创建常量指针,该指针一旦指向一个地址,就不能再指向别处:1 | double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5}; |
const 也可以用于创建既不能更改所指向地址,也不能修改指向地址上的值的指针:1 | double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5}; |
1 | /* order.c -- 指针运算中的优先级 */ |
1 | *p1 = 100, *p2 = 100, *p3 = 300 |
两种声明几乎相同:
1 | const char *pt1 = "Something is pointing at me;"; |
ar1+1等操作,但不允许进行++ar1这样的操作。总结:初始化数组把静态存储区的字符串拷贝到数组中,而初始化指针只把字符串的地址拷贝给指针。
空字符 (‘\0’):
空指针 (NULL):
注意:
虽然它们可以用数值0来表示,但从概念上看,空字符和空指针是不同类型的0。
char *strcpy(char * restrict s1, const char * restrict s2);
该函数把s2指向的字符串(包括空字符)拷贝至s1指向的位置,返回值是s1。
char *strncpy(char * restrict s1, const char * restrict s2, size_t n);
该函数把s2指向的字符串拷贝至s1指向的位置,拷贝的字符数不超过n,其返回值是s1。该函数不会拷贝空字符后面的字符,如果源字符串的字符少于n个,目标字符串就以拷贝的空字符结尾;如果源字符串有n个或超过n个字符,就不拷贝空字符。
char *strcat(char * restrict s1, const char * restrict s2);
该函数把s2指向的字符串拷贝至s1指向的字符串末尾。s2字符串的第1个字符将覆盖s1字符串末尾的空字符。该函数返回s1。
char *strncat(char * restrict s1, const char * restrict s2, size_t n);
该函数把s2字符串中的n个字符拷贝至s1字符串末尾。s2字符串的第1个字符将覆盖s1字符串末尾的空字符。不会拷贝s2字符串中空字符和其后的字符,并在拷贝字符的末尾添加一个空字符。该函数返回s1。
int strcmp(const char * s1, const char * s2);
如果s1字符串在机器排序序列中位于s2字符串的后面,该函数返回一个正数;如果两个字符串相等,则返回0;如果s1字符串在机器排序序列中位于s2字符串的前面,则返回一个负数。
int strncmp(const char * s1, const char * s2, size_t n);
该函数的作用和strcmp()类似,不同的是,该函数在比较n个字符后或遇到第1个空字符时停止比较。
char *strchr(const char * s, int c);
如果s字符串中包含c字符,该函数返回指向s字符串首位置的指针(末尾的空字符也是字符串的一部分,所以在查找范围内);如果在字符串s中未找到c字符,该函数则返回空指针。
char *strpbrk(const char * s1, const char * s2);
如果 s1 字符中包含 s2 字符串中的任意字符,该函数返回指向 s1 字符串首位置的指针;如果在s1字符串中未找到任何s2字符串中的字符,则返回空字符。
char *strrchr(const char * s, int c);
该函数返回s字符串中c字符的最后一次出现的位置(末尾的空字符也是字符串的一部分,所以在查找范围内)。如果未找到c字符,则返回空指针。
char *strstr(const char * s1, const char * s2);
该函数返回指向s1字符串中s2字符串出现的首位置。如果在s1中没有找到s2,则返回空指针。
size_t strlen(const char * s);
该函数返回s字符串中的字符数,不包括末尾的空字符。
1 | char * s_get(char * st, int n) |
1 | #include <stdio.h> // 提供 fgets()和getchar()的原型 |
malloc()函数会找到合适的空闲内存块,这样的内存是匿名的。也就是说, malloc()分配内存,但是不会为其赋名。然而,它确实返回动态分配内存块的首字节地址。因此,可以把该地址赋给一个指针变量,并使用指针访问这块内存。malloc()函数可用于返回指向数组的指针、指向结构的指针等,所以通常该函数的返回值会被强制转换为匹配的类型。如果 malloc()分配内存失败,将返回空指针。
1 | double * ptd; |
以上代码为30个double类型的值请求内存空间,并设置ptd指向该位置。
注意,指针ptd被声明为指向一个double类型,而不是指向内含30个double类型值的块。
1 | double item[n]; |
静态内存的数量在编译时是固定的,在程序运行期间也不会改变。自动变量使用的内存数量在程序执行期间自动增加或减少。但是动态分配的内存
数量只会增加,除非用 free()进行释放。
1 | ... |
1 | long * newmem; |
和malloc()类似,返回指向void的指针。如果要储存不同的类型,应使用强制类型转换运算符。
calloc()函数还有一个特性:它把块中的所有位都设置为0(注意,在某些硬件系统中,不是把所有位都设置为0来表示浮点值0)。
free()函数也可用于释放calloc()分配的内存。
对多维数组而言,使用变长数组更方便。当然,也可以用 malloc() 创建二维数组,但是语法比较繁琐。如果编译器不支持变长数组特性,就只能固
定二维数组的维度,如下所示:
1 | int n = 5; |
先复习一下指针声明。由于malloc()函数返回一个指针,所以p2必须是一个指向合适类型的指针。第1个指针声明:int (* p2)[6]; // C99之前的写法
表明p2指向一个内含6个int类型值的数组。因此,p2[i]代表一个由6个整数构成的元素,p2[i][j]代表一个整数。
第2个指针声明用一个变量指定p3所指向数组的大小。因此,p3代表一个指向变长数组的指针,这行代码不能在C90标准中运行。
.png)
为了避免读到空文件,应该使用入口条件循环(不是do while循环)。鉴于getc() (和其他C输入函数)的设计,程序应该在进入循环体之前先尝试读取。1
2
3
4
5
6
7
8
9
10// 设计范例 #1
int ch; // 用int类型的变量储存EOF
FILE * fp;
fp = fopen("wacky.txt", "r");
ch = getc(fp); // 获取初始输入
while (ch != EOF)
{
putchar(ch); // 处理输入
ch = getc(fp); // 获取下一个输入
}
以上代码可简化为:
1 | // 设计范例 #2 |

fseek()的第1个参数是FILE指针,指向待查找的文件,fopen()应该已打开该文件。
fseek()的第2个参数是偏移量(offset)。该参数表示从起始点开始要移动的距离(参见表列出的起始点模式)。该参数必须是一个long类型的值,可以为正(前移)、负(后移)或0(保持不动)。
fseek()的第3个参数是模式,该参数确定起始点。
下面是调用fseek()函数的一些示例,fp是一个文件指针:
fseek(fp, 0L, SEEK_SET); // 定位至文件开始处
fseek(fp, 10L, SEEK_SET); // 定位至文件中的第10个字节
fseek(fp, 2L, SEEK_CUR); // 从文件当前位置前移2个字节
fseek(fp, 0L, SEEK_END); // 定位至文件结尾
fseek(fp, -10L, SEEK_END); // 从文件结尾处回退10个字节

如果him == &fellow[0],那么*him == fellow[0],因为&和*是一对互逆运算符。
因此,可以做以下替代:fellow[0].income == (*him).income
必须要使用圆括号,因为.运算符比*运算符的优先级高。
总之,如果him是指向guy类型结构barney的指针,下面的关系恒成立:barney.income == (*him).income == him->income // 假设 him == &barney
移位运算符针对2的幂提供快速有效的乘法和除法:
number << n number 乘以2的n次幂number >> n 如果number为非负,则用number除以2的n次幂
这些移位运算符类似于在十进制中移动小数点来乘以或除以10。
因为ASCII码只使用最后7位,所以有时需要用掩码关闭其他位,其相应的二进制掩码是什么?分别用十进制、八进制和十六进制来表示这个掩码。
掩码的二进制是1111111;十进制是127;八进制是0177;十六进制是0x7F。
#ifdef 判断是否定义了标识符。如果定义了则执行#else或#endif指令之前的所有指令并编译所有C代码,如果未定义则执行#else和#endif指令之间的所有代码。
#ifndef 判断后面的标识符是否是未定义的。通常用于防止多次包含一个文件。
#if指令很像C语言中的if。#if后面跟整型常量表达式,如果表达式为非零,则表达式为真。
可以按照if else的形式使用#elif。
较新的编译器提供另一种方法测试名称是否已定义,即用#if defined(VAX)代替#ifdef VAX。
这里,defined是一个预处理运算符,如果它的参数是用#defined定义过,则返回1;否则返回0。这种新方法的优点是,它可以和#elif一起使用。1
2
3
4
5
6
7
8
9#if defined (IBMPC)
#include "ibmpc.h"
#elif defined (VAX)
#include "vax.h"
#elif defined (MAC)
#include "mac.h"
#else
#include "general.h"
#endif
如果在VAX机上运行这几行代码,那么应该在文件前面用下面的代码定义VAX:#define VAX
#define 中使用参数在#define中使用参数可以创建外形和作用与函数类似的类函数宏。带有参数的宏看上去很像函数,因为这样的宏也使用圆括号。类函数宏定义的圆括号中可以有一个或多个参数,随后这些参数出现在替换体中。

#define SQUARE(X) X*X#运算符下面是一个类函数宏:#define PSQR(X) printf("The square of X is %d.\n", ((X)*(X)));
假设这样使用宏:PSQR(8);
输出为:The square of X is 64.
C允许在字符串中包含宏参数。在类函数宏的替换体中,#号作为一个预处理运算符,可以把记号转换成字符串。例如,如果x是一个宏形参,那么#x就是转换为字符串”x”的形参名。这个过程称为字符串化(stringizing)。1
2
3
4
5
6
7
8
9
10
11
12
13/* subst.c -- 在字符串中替换 */
#include <stdio.h>
#define PSQR(x) printf("The square of " #x " is %d.\n",((x)*(x)))
int main(void)
{
int y = 5;
PSQR(y);
PSQR(2 + 4);
return 0;
}
该程序的输出如下:
The square of y is 25.
The square of 2 + 4 is 36.
调用第1个宏时,用”y“替换#x。调用第2个宏时,用”2 + 4“替换#x。ANSI C字符串的串联特性将这些字符串与printf()语句的其他字符串组合,生成最终的字符串。
##运算符与#运算符类似,##运算符可用于类函数宏的替换部分。而且,##还可用于对象宏的替换部分。##运算符把两个记号组合成一个记号。例如,可以这样做:#define XNAME(n) x ## n
然后,宏XNAME(4)将展开为x4。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// glue.c -- 使用##运算符
#include <stdio.h>
#define XNAME(n) x ## n
#define PRINT_XN(n) printf("x" #n " = %d\n", x ## n);
int main(void)
{
int XNAME(1) = 14; // 变成 int x1 = 14;
int XNAME(2) = 20; // 变成 int x2 = 20;
int x3 = 30;
1212
PRINT_XN(1); // 变成 printf("x1 = %d\n", x1);
PRINT_XN(2); // 变成 printf("x2 = %d\n", x2);
PRINT_XN(3); // 变成 printf("x3 = %d\n", x3);
return 0;
}
该程序的输出如下:
x1 = 14
x2 = 20
x3 = 30
...和__VA_ARGS__stdvar.h 头文件提供了工具,让用户自定义带可变参数的函数。
通过把宏参数列表中最后的参数写成省略号(即,3个点…)来实现这一功能。这样,预定义宏_ _VA_ARGS_可用在替换部分中,表明省略号代表什么。例如,下面的定义:
`#define PR(…) printf( VA_ARGS _)`
假设稍后调用该宏:1
2
3
4
5PR("Howdy");
PR("weight = %d, shipping = $%.2f\n", wt, sp);
对于第1次调用,_ _VA_ARGS_ _展开为1个参数:"Howdy"。
对于第2次调用,_ _VA_ARGS_ _展开为3个参数:"weight = %d,
shipping = $%.2f\n"、wt、sp。
因此,展开后的代码是:1
2printf("Howdy");
printf("weight = %d, shipping = $%.2f\n", wt, sp);
宏的一个优点是,不用担心变量类型(这是因为宏处理的是字符串,而不是实际的值)。
对于简单的函数,程序员通常使用宏,如下所示:1
2
3#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
#define ABS(X) ((X) < 0 ? -(X) : (X))
#define ISSIGN(X) ((X) == '+' || (X) == '-' ? 1 : 0)
(如果x是一个代数符号字符,最后一个宏的值为1,即为真。)
用圆括号把宏的参数和整个替换体括起来。这样能确保被括起来的部分在下面这样的表达式中正确地展开:forks = 2 * MAX(guests + 3, last);
用大写字母表示宏函数的名称。

最简单的方法是使用函数说明符 inline 和存储类别说明符static。通常,内联函数应定义在首次使用它的文件中,所以内联函数也相当于函数原型。
编译器优化内联函数必须知道该函数定义的内容。这意味着内联函数定义与函数调用必须在同一个文件中。最简单的做法是,把内联函数定义放入头文件,并在使用该内联函数的文件中包含该头文件即可。1
2
3
4
5
6
7
8
9// eatline.h
#ifndef EATLINE_H_
#define EATLINE_H_
inline static void eatline()
{
while (getchar() != '\n')
continue;
}
#endif
一般都不在头文件中放置可执行代码,内联函数是个特例。因为内联函数具有内部链接,所以在多个文件中定义同一个内联函数不会产生什么问题。

stdarg.h必须按如下步骤进行:
因为va_arg()不提供退回之前参数的方法,所以有必要保存va_list类型变量的副本。
1 | va_list ap; // 声明一个对象储存参数 |
本节主要介绍一些文本处理相关的linux命令,是日后信息安全工作中的情报侦察、数据梳理、日志分析等技术的重要基础。
awk是一种处理文本文件的语言,是一个强大的文本分析工具。
相比较屏幕处理的优点,awk在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息。 awk也是一个非常棒的数据处理工具!相较于sed常常作用于一整个行的处理, awk则比较倾向于一行当中分成数个字段来处理。
awk处理过程: 依次对每一行进行处理,然后输出。
命令的基本格式是:awk ‘{print($1)}’
案例:
1 | cat 1.txt|awk '{print($2)}' #打印出文本中每行的第二个字段 |


1 | cat /etc/passwd |awk -F ":" '{print($1)}' #-F指定分隔符,以“:”为分隔符,打印出文本中每行第一个字段 |


1 | cat /etc/passwd |awk -F ":" '{print($1,"+++",$3)}' #打印出第一个字段与第三个字段,并在其中间添加+++内容 |


1 | cat passwd | awk -F ":" '{print $NF}' #打印出文本中每行的最后一个字段 |
sed 是stream editor(流编辑器)的简称,是一款强大的,并且有些复杂的程序。
sed 本身是一个管线命令,可以将数据进行替换、删除、新增、提取特定行等功能,主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。sed按行来执行命令。
接下来的实验,我们要以/etc/passwd文件内容为例子。
拷贝文件cat /etc/passwd > 1.txt
我们以一个例子来介绍sed命令,sed 's/:/+/g'
s代表搜索,g代表的则是全文。不加g的话则是替换每行第一个出现的。所以这条命令代表的将文件中所有的:替换为+
cat 1.txt |sed 's/:/+/3g' #代表的则是从第三次匹配开始替换,依此类推。
sed -i 's/:/+/g' 1.txt #参数-i,直接修改文件,并非打印出来。
以上这些命令中是以/做为该命令的定界符,如果需要修改或者匹配的字段带有/则不能再使用/做为该条命令的定界符,可以使用任意的定界符,不冲突就行。cat passwd |sed 's!:!/!g' 以!号做为定界也是一样的。
此外,在此命令中,搜索位置使用^代表一行的开头,$代表一行的结尾。sed是以行为单位执行的。
我们用如下案例展示:
cat ip.txt |sed 's#^#http://#g'在每行之前加上http://。
cat ip.txt |sed 's#$#:8080#g'在每行末尾加上“:8080”
sed '/^$/d' 2.txt删除空白的行。
sed '/^s/'d 删除文件中所有开头是s的行。
grep这个名字来自于短语“global regular expression print”,所以我们能看出 grep 程序和正则表达式有关联。
本质上,grep 程序会在文本文件中查找一个指定的正则表达式,并把匹配行输出到标准输出。
命令格式: grep [options] [pattern] [filename]
案例:
1 | cat passwd |grep root # 只显示带有root关键字的行 |
find 命令用来在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名。如果使用该命令时,不设置任何参数,则 find 命令将在当前目录下查找子目录与文件。并且将查找到的子目录和文件全部进行显示。
这是一个超级复杂的命令,最简单的模式为:find 路径 -name [名字]。
案例:
1 | find / -size 1000k #从根目录查找大小为1000K的文件 |
1 | find 搜索路径 [选项] 搜索内容 -exec 命令2{}\; |
ag类似grep和find,但是执行效率比后两者高。
最基本的用法为ag -g <File Name>,从当前目录寻找文件
案例:
1 | ag -g <File Name> # 类似于 find . -name <File Name> |
用于显示文件信息:
用法:stat [文件或目录]
split命令可以将一个大文件分割成很多个小文件,有时需要将文件分割成更小的片段,比如为提高可读性,生成日志等。
参数:
案例:
1 | split -b 30k messages # 按照每个文件30K大小切割messages文件。 |
sort命令是在Linux里非常有用,它将文件进行排序,并将排序结果标准输出。
1 | cat 1.txt|sort #默认的排序方式,从首字母开始 |
uniq用于报告或忽略文件中的重复行,一般与sort命令结合使用。
要注意先排序,后去重。因为uniq命令只能消除相邻且相同的行。
1 | cat 1.txt |sort -n |uniq #排序去重 |
以上系列命令可以用于日志分析:
如统计IP地址的访问数量并按照数量进行排序: cat access_log |awk '{print($1)}'|sort|uniq -c |sort -nr |more
针对访问量最大的IP分析其访问内容: cat access_log |grep 'IP地址'|head -n 100
nl命令用于显示文件内容行号。
案例:
ssh命令是Linux的远程连接工具。
ssh命令:Linux的远程连接工具
例如ssh 192.168.0.149,以当前终端用户身份远程连接IP为192.168.0.149的计算机。
ssh -l farmsec 192.168.0.149以farmsec用户远程登录,也可写成ssh farmsec@192.168.0.149执行ssh的默认情况下,会使用本地的用户名连接对方的用户名,例如本地的root连接对面服务器的root,如果两端用户名不一样,则需要使用-l username指定用户名。
再确认密码后,方能登录成功,而同时变化的文件为:/root/.ssh/known_hosts 在第一次登录时,openssh将会提示不知道这台登录的主机,只要输入yes,就会把这台主机的“识别信息”添加到known_hosts文件中. 第二次登陆后,则无须此步骤,但是如果出现错误,例如主机产生变化,则需要删除/root/.ssh/known_hosts内容。
Smb服务为网络文件共享协议,它允许应用程序和终端用户从远端的文件服务器访问文件资源 用法:在kali的文件–>其他位置-->连接到服务器处输入:smb://192.168.0.149/share
关于此部分知识,还将在后续有关windows的章节中详述。
rdesktop命令用于在linux下链接windows远程桌面。
用法:rdesktop 192.168.0.178
关于此部分知识,还将在后续有关windows的章节中详述。
URL,既是俗称的网址/网页链接。
curl命令是一个利用URL规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称curl为下载工具。作为一款强力工具,curl支持包括HTTP、HTTPS、ftp等众多协议,还支持POST、cookies、认证、从指定偏移处下载部分文件、用户代理字符串、限速、文件大小、进度条等特征。
用法:curl [选项] [url]
参数:
案例:
1 | curl www.farmsec.com |
wget命令:用来从指定的URL下载文件。wget非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性,如果是由于网络的原因下载失败,wget会不断的尝试,直到整个文件下载完毕。如果是服务器打断下载过程,它会再次联到服务器上从停止的地方继续下载。这对从那些限定了链接时间的服务器上下载大文件非常有用。
1 | wget https://bootstrap.pypa.io/get-pip.py |
前面我们下载下的文件名都是乱码,所以我们需要给它自定义文件名 加入-O的参数即可指定文件名
1 | wget https://bootstrap.pypa.io/get-pip.py -O 123.py |
如果文件比较大时,加入参数-b,进行后台下载,然后可使用使用tail -f wget-log查看进度。
1 | -c:继续执行上次终端的任务;可以在下载中断后再次使用。 |
Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.
1 | $ hexo new "My New Post" |
More info: Writing
1 | $ hexo server |
More info: Server
1 | $ hexo generate |
More info: Generating
1 | $ hexo deploy |
More info: Deployment
`的使用
—dbms=DBMS Force back-end DBMS to provided value
—prefix=PREFIX Injection payload prefix string
—suffix=SUFFIX Injection payload suffix string
—technique=TECH.. SQL injection techniques to use (default “BEUSTQ”)
—batch Never ask for user input, use the default behavior
意外情况
—risk=RISK Risk of tests to perform (1-3, default 1)
—second-url=SEC.. Resulting page URL searched for second-order response
—chunked Use HTTP chunked transfer encoded (POST) requests
—hpp Use HTTP parameter pollution method
—tamper=TAMPER Use given script(s) for tampering injection data
结尾
—is-dba Detect if the DBMS current user is DBA
—dbs Enumerate DBMS databases
—current-user Retrieve DBMS current user
—current-db Retrieve DBMS current database
-D -T -C —dump
网站time
—timeout=TIMEOUT Seconds to wait before timeout connection (default 30)
—time-sec=TIMESEC Seconds to delay the DBMS response (default 5)
-u “url” —cookie
-r cookie
-u —cookie=” “ —data=” “
-p --dbms
time tamper
—dbs
1 | -D xxx --tables |
—is-dba —currxxxx-user
-iL xx.txt
-sV # 服务版本
-p # 端口
-A -Pn 0-65536 # -A 全扫描 -Pn 直接发包
-iL xxx.txt
-sP # 探测此网段的存活主机 可以连同 | grep nmap 一起使用 这样看起来更清爽一些
-p 80 # 检测特定端口
-sT # 检测TCP端口
-sU # 检测UDP端口
-sV # 探测端口的服务和版本
-O # 探测操作系统的类型和版本
-Pn # 直接进行深度测试,不检测主机存活
1 | masscan -p 1-65535 192.168.0.0/24 --rate=2000 >>ports.txt |