c语言
嵌入式阅读 结合关系一般自左向右,单目+-和赋值=自右向左\这样的表达式太复杂,不容易阅读和理解
#include <stdio.h>
在 C 语言中,sizeof () 是一个判断数据类型或者表达式长度的运算符。
整数类型
char:一字节 8比特
short: 2字节
int:取决于编译器CPU,通常的意义是一个字
long:取决于编译器,通常是两个字
longlong
一台计算器的字长是指当前计算器reg(寄存器)的宽度
寄存器一次可以处理的数据=int 一个字
int就是用来表达寄存器的
如何表示负数
二进制负数
一个字节可以表达的数:
00000000-11111111(0-255)
三种方案:
1,仿照十进制,有一个特殊的标志表示负数
- 取中间的数为0,如1000000表示0,比它小的是负数,比它大的是正数
- 补码
补码:对于-a,其补码就是0-a,实际上是2的n次方-a,n是这种类型的位数
补码的意义就是拿补码和原码可以加出一个溢出的”零”
整数的输入输出
只有两种形式:int或long long
%d:int
%u:unsigned
%ld:long long
%lu:unsigned long long
一个以0开始的数字字面量是8进制
一个以0x开始的数字字面量是16进制
16进制很适合表达二进制数据,因为4位二进制正好是一个16进制位
8进制的一位数字正好表达3位二进制
因为早期计算机的字长是12的倍数,而非8
带小数点的字面量是double而非float
float需要用f或F后缀来表明身份
*浮点数内部表达
浮点数在计算时是由专用的硬件部分实现的
计算double和float所用的部件是一样的
^字符类型
char是一种整数,也是一种特殊的类型:字符.这是因为:
用单引号表示的字符字面量:’a’
‘’也是一个字符
printf和scanf里用%c来输入输出字符
*逃逸字符
用来表达无法印出来的控制字符或特殊字符,它由一个反斜杠”\”开头,后面跟上另一个字符,这两个字符合起来,组成了一个字符
不同的shell做出不同的反应
||优先级大于&&
count[x]++;
*取地址运算
sizeof
是一个运算符,给出某个类型或变量在内存中所占据的字节数
运算符&
·scanf(“%d”,&i);里的&
·获得变量的地址,他的操作数必须是变量
指针
就是保存地址的变量
int*P=&i
变量的值是内存的地址
·普通变量的值是实际的值
·指针变量的值是具有实际值的变量的地址
访问那个地址上的变量*
* 是一个单目运算符,用来访问指针的值所表示的地址上的变量
·可以做右值也可以做左值(可以放等号两边)
·int k=*p;
·*p=k+1
^指针的使用
一
·交换两个变量的值
二
·函数返回多个值,某些值就只能通过指针返回
·传入的参数实际上是需要保存带回的结果的变量
函数返回运算的状态,结果通过指针返回
常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错:
-1或0
但是当任何数值都是有效的可能结果是,就得分开返回了
·后续的语言(c++,java)采用了异常机制来解决这个问题
*指针最常见的错误
·定义了指针变量,还没有指向任何变量,就开始使用指针
传入函数的数组成了什么?
·函数参数表中的数组实际上是指针
·sizeof(a)==sizeof(int*)
·但是可以用数组的运算符[]进行运算
数组变量是特殊的指针
·数组变量本身表达地址所以
·int a[10];int*p=a;//无需用&取地址
·但是数组的单元表达的是变量,需要用&取地址
·a==&a[0]
·[]运算符可以对数组做,也可以对指针做:
·数组变量是const的指针,所以不能被赋值
·int a[]<==>int *const a=.....
C语言const的用法详解,C语言常量定义详解 (biancheng.net)
判断哪个被const了的标志是const在*的前面还是后面
^保护数组值
·因为把数组传入函数时,传递的是地址,所以那个函数内部可以修改数组的值
·为了保护数组不被函数破坏,可以设置参数为const
·int sum (const int a[],int length);
·给一个指针加1表示要让指针指向下一个变量
int a[10];
int*p=a;
*(p+1)-->a[1]
·如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义
*p++
·取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
·*的优先级虽然高,但是没有++高
·常用于数组类的连续空间操作
·在某些CPU上,这可以直接被翻译成一条汇编指令
0地址
·当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
·所以你的指针不应该具有0值
·因此可以用0地址来表示特殊的事情
·返回的指针是无效的
·指针没有被真正初始化(先初始化为0)
·NULL是一个预定定义的符号,表示0地址
·有的编译器不愿意用0来表示0地址
指针的类型
·无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
·但是指向不同类型的指针是不能直接互相赋值的
·这是为了避免用错指针
指针类型转换
·void*表示不知道指向什么东西的指针
·计算时与char*相同(但不相通)
·指针也可以转换类型
·int*p=&i’;void*q=(void*)p;
·这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量
·我不再当你是int,我认为你就是个void!
size_t类型表示c中任何对象所能达到的最大长度,它是无符号整数.
字符串
·以0(整数0)结尾的一串字符
·0或’\0’是一样的,但是和’0’不同
·0标志字符串的结束,但它不是字符串的一部分
·计算字符串长度的时候不包含这个0
·字符串以数组的形式存在,以数组或指针的形式访问
·更多的是以指针的形式
·string.h里有很多处理字符串的函数
字符串变量
·char*str=”hello”;
·char word[]=”Hello”;
·char line[10]=”Hello”;
·Hello会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0
·两个相邻的字符串常量会被自动连接起来
·C语言的字符串是以字符数组的形态存在的
·不能用运算符对字符串做运算
·通过数组的方式可以遍历字符串
·唯一特殊的地方是字符串字面量可以用来初始化字符数组
%*c表示跳过这个字符
%作为引导符,控制输入输出的格式.
%s表示输出字符串形式
%f表示输出为浮点型数据
%o表示以8进制形式输出整数
%d表示输出整形数据
%ld表示输出为长整型数据
%u表示输出格式为无符号数据
%c表示输出字符数据(char类型)
%md中m指定输出字段的宽度
%x表示以16进制形式输出整数
如果需要修改字符串,应该用数组
char*中只有它所指的字符数组有结尾的0,才能说它所指的是字符串
字符串输如输出
·scanf不安全,
若要可以在%和s间加数字例如%7s,限制读取的位数
程序参数
·int main(int argc,char const*argv[])
·argv[0]是命令本身
·当使用Unix的符号链接时,反映符号链接的名字
处理字符串需要用
string.h
·strlen<·size_t strlen(const char*s)
·返回s的字符串长度(不包括结尾的0)>
·strcmp<·两个字符串作比较,返回值是两个不相等的字符的差值>
·strcpy< ·char*strcpy(char*restrict dst, const char*restrict stc);
·把src的字符串拷贝到dst
·restrict表明src和dst不重叠(C99)
·返回dst
·为了能链起代码来>
·strcat<char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾>
·strchr<字符串搜索函数(字符串中找字符)
·char*strchr(const char*s,int c);
·char*strrchr(const char*s, int c);
·返回NULL表示没有找到
·strstr
枚举
·枚举是一种用户定义的数据类型,它用关键字enum以如下语法来声明:enum枚举类型名字{名字0,...名字n}
·枚举类型名字通常并不真的使用,要用的是在大括号里的名字,因为他们就是常量符号,它们的类型是int,值则依次从0到n.如:enum colors{red,yellow,green};
枚举比宏好,因为枚举有int类型
结构类型
声明结构的形式
①struct point{
int x;
int y;
};
struct point p1,p2;
p1和p2都是point里面有x和y的值
②struct{
int x;
int y;
}p1,p2;
p1和p2都是一种无名结构,里面有x和y
③struct point{
int x;
int y;
}p1,p2;
p1和p2都是point里面有x和y的值t
对于第一和第三种形式,都声明了结构point.但是第二种形式没有声明point,只是定义了两个变量.
结构成员
·结构和数组有点像
·数组用[]运算符和下标访问其成员
·a[0]=10;
·结构用.运算符和名字访问其成员
·today.day
·student.firstName
·pl.x
·pl.y
结构运算
·要访问整个结构,直接用结构变量的名字
·对于整个结构,可以做赋值\取地址,也可以传递给函数参数
·pl=(struct point){5,10};//相当于pl.x=5;
pl.y=10
·pl=p2;//相当于pl.x=p2.x;pl.y=p2.y;
结构指针
·和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符
·struct date*pDate=&today;
____________________________________________________
结构作为函数参数
int numberOfDays(struct date d)
·整个结构可以作为参数的值传入函数
·这时候是在函数内新建一个结构变量,并复制调用者的结构的值
·也可以返回一个结构
·这与数组完全不同
如果有变量定义:
struct rectangle r,*rp;
rp=&r;
那么下面的四种形式是等价的:
r.ptl.x
rp->ptl.x
(r.ptl).x
(rp->ptl).x
但是没有rp->ptl->x
ACLLib Windows API
·main()成为c语言的入口函数其实和c语言本身无关,你的代码是被一小段叫做启动代码的程序所调用的,它需要一个叫做main的地方
·操作系统把你的可执行程序装载到内存里,启动运行,然后调用你的main函数
产生一个窗口---->窗口结构
在窗口里画东西---->DC
得到用户的鼠标和键盘动作---->消息循环和消息处理代码
____________________________________________________
自定义数据类型(typedef)
·c语言提供了一个叫做typedef的功能来声明一个已有的数据类型的新名字比如:
typedef int Length;
使得Length成为int类型的别名
·这样,Length这个名字就可以代替int出现在变量定义和参数声明的地方了
Length a,b,len;
Length numbers[10];
____________________________________________________
联合 -union
____________________________________________________
静态本地变量
·在本地变量定义时加上static修饰符就成为静态本地变量
·当函数离开的时候,静态本地变量会继续存在并保持其值
·静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值
·静态本地变量实际上是特殊的全局变量
·它们位于相同的内存区域
·静态本地变量具有全局的生存期,函数内的局部作用域
·static在这里的意思是局部作用域(本地可以访问)
____________________________________________________
*返回指针的函数
·返回本地变量的地址是危险的
·返回全局变量或静态本地变量的地址是安全的
·返回在函数内malloc的内存是安全的,但是容易造成问题
·最好的做法是返回传入的指针
编译预处理指令
·#开头的是编译预处理指令
·它们不是C语言的成分,但是c语言程序离不开它们
·#define用来定义一个宏
·#define<名字><值>
·注意没有结尾的分号,因为不是c的语句
·名字必须是一个单词,值可以是各种东西
·在c语言的编译器开始编译之前,编译预处理程序(cpp)会把程序中的名字换成值
·完全的文本替换
·gcc-save-temps
宏
·如果一个宏的值中有其他的宏的名字,也是会被替换的
·如果一个宏的值超过一行,最后一行之前的行末需要加\
·宏的值后面出现的注释不会被当作宏的值的一部分
预定义的宏
·_LINE_ 源代码行号
·_FILE_ 源代码全路径
·_DATE_ 时间
·_TIME_
·_STDC_
带参数的宏
·#define cube(x)((x)*(x)*(x))
·宏可以带参数
项目
·在Dev C++中新建一个项目,然后把几个源代码文件加入进去
· 对于项目,Dev C++的编译会把一个项目中所有的源代码文件都编译后,链接起来
·有的IDE有分开的编译和构建两个按钮,前者是对单个源代码文件编译,后者是对整个项目做链接
头文件
·把函数原型放到一个头文件(以.h结尾)中,在需要调用这个函数的源代码文件(.c文件)中#include这个头文件,就能让编译器在编译的时候知道函数的原型
自己的头文件用””,系统给的用<>.
·#include不是用来引入库的
·stdio.h里只有printf的原型,printf的代码在另外的地方,某个.lib(Windows)或.a(Unix)中
·现在的c语言编译器默认会引入所有的标准库
·#include<stdio.h>只是为了让编译器知道printf函数的原型
保证你调用时给出的参数值是正确的类型
不对外公开的函数
·在函数前加上static就使得它成为只能在所在的编译单元中被使用的函数
·在全局变量前面加上static就使得它成为只能在所在的编译单元中被使用的全局变量
声明
int i;是变量的定义
extern int i;是变量的声明
格式化的输入输出
%[flags][width][.prec][hil]type
scanf:%[flag]type
文本vs二进制
·Unix喜欢用文本文件来做数据存储和程序配置
·交互式终端的出现使得人们喜欢用文本和计算机”talk”
·Unix的shell提供了一些读写文本的小程序
·Windows喜欢用二级制文件
·DOS是草根文化,并不继承和熟悉Unix文化
·PC刚开始的时候能力有限,DOS的能力更有限,二进制更接近底层
·文本的优势是方便人类读写,而且跨平台
·文本的缺点是程序输入输出要经过格式化,开销大
·二进制的缺点是人类读写困难,而且不跨平台
·int 的大小不一致,大小端的问题.....
·二进制的优点是程序读写快
二级制读写用fread 和fwrite
在文件中定位
·long ftell(FILE*stream);
·int fseek(FILE*stream,long offset, int whence);
·SEEK_SET:从头开始
·SEEK_CUR:从当前位置开始
·SEEK_END:从尾开始(倒过来)
·这样的二进制文件不具有可移植性
·在int为32位的机器上写成的数据文件无法直接在int为64位的机器上正确读出
·解决方案之一是放弃使用int,而是typedef具有明确大小的类型
·更好的方案是用文本
·其实所有的文件最终都是二进制的
·文本文件无非是用最简单的方式可以读写的文件
·more\tail
·cat
·vi
·而二进制文件是需要专门的程序来读写的文件
·文本文件的输入输出是格式化\可能经过转码
按位运算(把整数当二进制进行按位运算)
逻辑运算vs按位运算
·对于逻辑运算,它只看到两个值:0和1
·可以认为逻辑运算相当于把所有非0值都变成1,然后做按位运算
对一个数做两次异或运算则还是原来那个数
左移<<
·i<<j
·i中所有的位向左移动j个位置,而右边填入0
·所有小于int的类型,移位以int的方式来做,结果是int
·x<<1等价于x*=2
·x<<=n等价于x*=
关于结构体类型的定义的总结;
一般格式就是;
struct 结构体名(也就是可选标记名)
{
成员变量;
信息安全( 1,传统的网络安全2.云安全3公共安全)
什么是信息安全?保护数据
2017年wannacry病毒席卷全球/撞库/暗网:隐匿性.洋葱路由不可追踪/表层网络,深网,暗网/
熟悉web渗透测试方法和功放技术,包括SQL注入.xss跨站.CSRF伪造请求.命令执行等owasp top10安全漏洞与防御,有一定的漏洞分析和挖掘能力;
安全岗位核心技能需求:
熟悉Linux,Windows不同平台的渗透测试,了解常用web框架.数据库,中间件,和操作系统 弱点以及相关攻防技术;
熟悉国内外主流安全工具包括Kali Linux,metasploit,Nessus,nmap.awvs,burp,appscan等
熟悉一门编程语言,有一定的代码编写能力;
Wireshark
c语言
嵌入式阅读 结合关系一般自左向右,单目+-和赋值=自右向左\这样的表达式太复杂,不容易阅读和理解
#include <stdio.h>
在 C 语言中,sizeof () 是一个判断数据类型或者表达式长度的运算符。
整数类型
char:一字节 8比特
short: 2字节
int:取决于编译器CPU,通常的意义是一个字
long:取决于编译器,通常是两个字
longlong
一台计算器的字长是指当前计算器reg(寄存器)的宽度
寄存器一次可以处理的数据=int 一个字
int就是用来表达寄存器的
如何表示负数
二进制负数
一个字节可以表达的数:
00000000-11111111(0-255)
三种方案:
1,仿照十进制,有一个特殊的标志表示负数
- 取中间的数为0,如1000000表示0,比它小的是负数,比它大的是正数
- 补码
补码:对于-a,其补码就是0-a,实际上是2的n次方-a,n是这种类型的位数
补码的意义就是拿补码和原码可以加出一个溢出的”零”
整数的输入输出
只有两种形式:int或long long
%d:int
%u:unsigned
%ld:long long
%lu:unsigned long long
一个以0开始的数字字面量是8进制
一个以0x开始的数字字面量是16进制
16进制很适合表达二进制数据,因为4位二进制正好是一个16进制位
8进制的一位数字正好表达3位二进制
因为早期计算机的字长是12的倍数,而非8
带小数点的字面量是double而非float
float需要用f或F后缀来表明身份
*浮点数内部表达
浮点数在计算时是由专用的硬件部分实现的
计算double和float所用的部件是一样的
^字符类型
char是一种整数,也是一种特殊的类型:字符.这是因为:
用单引号表示的字符字面量:’a’
‘’也是一个字符
printf和scanf里用%c来输入输出字符
*逃逸字符
用来表达无法印出来的控制字符或特殊字符,它由一个反斜杠”\”开头,后面跟上另一个字符,这两个字符合起来,组成了一个字符
不同的shell做出不同的反应
||优先级大于&&
count[x]++;
*取地址运算
sizeof
是一个运算符,给出某个类型或变量在内存中所占据的字节数
运算符&
·scanf(“%d”,&i);里的&
·获得变量的地址,他的操作数必须是变量
指针
就是保存地址的变量
int*P=&i
变量的值是内存的地址
·普通变量的值是实际的值
·指针变量的值是具有实际值的变量的地址
访问那个地址上的变量*
* 是一个单目运算符,用来访问指针的值所表示的地址上的变量
·可以做右值也可以做左值(可以放等号两边)
·int k=*p;
·*p=k+1
^指针的使用
一
·交换两个变量的值
二
·函数返回多个值,某些值就只能通过指针返回
·传入的参数实际上是需要保存带回的结果的变量
函数返回运算的状态,结果通过指针返回
常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错:
-1或0
但是当任何数值都是有效的可能结果是,就得分开返回了
·后续的语言(c++,java)采用了异常机制来解决这个问题
*指针最常见的错误
·定义了指针变量,还没有指向任何变量,就开始使用指针
传入函数的数组成了什么?
·函数参数表中的数组实际上是指针
·sizeof(a)==sizeof(int*)
·但是可以用数组的运算符[]进行运算
数组变量是特殊的指针
·数组变量本身表达地址所以
·int a[10];int*p=a;//无需用&取地址
·但是数组的单元表达的是变量,需要用&取地址
·a==&a[0]
·[]运算符可以对数组做,也可以对指针做:
·数组变量是const的指针,所以不能被赋值
·int a[]<==>int *const a=.....
C语言const的用法详解,C语言常量定义详解 (biancheng.net)
判断哪个被const了的标志是const在*的前面还是后面
^保护数组值
·因为把数组传入函数时,传递的是地址,所以那个函数内部可以修改数组的值
·为了保护数组不被函数破坏,可以设置参数为const
·int sum (const int a[],int length);
·给一个指针加1表示要让指针指向下一个变量
int a[10];
int*p=a;
*(p+1)-->a[1]
·如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义
*p++
·取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
·*的优先级虽然高,但是没有++高
·常用于数组类的连续空间操作
·在某些CPU上,这可以直接被翻译成一条汇编指令
0地址
·当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
·所以你的指针不应该具有0值
·因此可以用0地址来表示特殊的事情
·返回的指针是无效的
·指针没有被真正初始化(先初始化为0)
·NULL是一个预定定义的符号,表示0地址
·有的编译器不愿意用0来表示0地址
指针的类型
·无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
·但是指向不同类型的指针是不能直接互相赋值的
·这是为了避免用错指针
指针类型转换
·void*表示不知道指向什么东西的指针
·计算时与char*相同(但不相通)
·指针也可以转换类型
·int*p=&i’;void*q=(void*)p;
·这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量
·我不再当你是int,我认为你就是个void!
size_t类型表示c中任何对象所能达到的最大长度,它是无符号整数.
字符串
·以0(整数0)结尾的一串字符
·0或’\0’是一样的,但是和’0’不同
·0标志字符串的结束,但它不是字符串的一部分
·计算字符串长度的时候不包含这个0
·字符串以数组的形式存在,以数组或指针的形式访问
·更多的是以指针的形式
·string.h里有很多处理字符串的函数
字符串变量
·char*str=”hello”;
·char word[]=”Hello”;
·char line[10]=”Hello”;
·Hello会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0
·两个相邻的字符串常量会被自动连接起来
·C语言的字符串是以字符数组的形态存在的
·不能用运算符对字符串做运算
·通过数组的方式可以遍历字符串
·唯一特殊的地方是字符串字面量可以用来初始化字符数组
%*c表示跳过这个字符
%作为引导符,控制输入输出的格式.
%s表示输出字符串形式
%f表示输出为浮点型数据
%o表示以8进制形式输出整数
%d表示输出整形数据
%ld表示输出为长整型数据
%u表示输出格式为无符号数据
%c表示输出字符数据(char类型)
%md中m指定输出字段的宽度
%x表示以16进制形式输出整数
如果需要修改字符串,应该用数组
char*中只有它所指的字符数组有结尾的0,才能说它所指的是字符串
字符串输如输出
·scanf不安全,
若要可以在%和s间加数字例如%7s,限制读取的位数
程序参数
·int main(int argc,char const*argv[])
·argv[0]是命令本身
·当使用Unix的符号链接时,反映符号链接的名字
处理字符串需要用
string.h
·strlen<·size_t strlen(const char*s)
·返回s的字符串长度(不包括结尾的0)>
·strcmp<·两个字符串作比较,返回值是两个不相等的字符的差值>
·strcpy< ·char*strcpy(char*restrict dst, const char*restrict stc);
·把src的字符串拷贝到dst
·restrict表明src和dst不重叠(C99)
·返回dst
·为了能链起代码来>
·strcat<char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾>
·strchr<字符串搜索函数(字符串中找字符)
·char*strchr(const char*s,int c);
·char*strrchr(const char*s, int c);
·返回NULL表示没有找到
·strstr
枚举
·枚举是一种用户定义的数据类型,它用关键字enum以如下语法来声明:enum枚举类型名字{名字0,...名字n}
·枚举类型名字通常并不真的使用,要用的是在大括号里的名字,因为他们就是常量符号,它们的类型是int,值则依次从0到n.如:enum colors{red,yellow,green};
枚举比宏好,因为枚举有int类型
结构类型
声明结构的形式
①struct point{
int x;
int y;
};
struct point p1,p2;
p1和p2都是point里面有x和y的值
②struct{
int x;
int y;
}p1,p2;
p1和p2都是一种无名结构,里面有x和y
③struct point{
int x;
int y;
}p1,p2;
p1和p2都是point里面有x和y的值t
对于第一和第三种形式,都声明了结构point.但是第二种形式没有声明point,只是定义了两个变量.
结构成员
·结构和数组有点像
·数组用[]运算符和下标访问其成员
·a[0]=10;
·结构用.运算符和名字访问其成员
·today.day
·student.firstName
·pl.x
·pl.y
结构运算
·要访问整个结构,直接用结构变量的名字
·对于整个结构,可以做赋值\取地址,也可以传递给函数参数
·pl=(struct point){5,10};//相当于pl.x=5;
pl.y=10
·pl=p2;//相当于pl.x=p2.x;pl.y=p2.y;
结构指针
·和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符
·struct date*pDate=&today;
____________________________________________________
结构作为函数参数
int numberOfDays(struct date d)
·整个结构可以作为参数的值传入函数
·这时候是在函数内新建一个结构变量,并复制调用者的结构的值
·也可以返回一个结构
·这与数组完全不同
如果有变量定义:
struct rectangle r,*rp;
rp=&r;
那么下面的四种形式是等价的:
r.ptl.x
rp->ptl.x
(r.ptl).x
(rp->ptl).x
但是没有rp->ptl->x
ACLLib Windows API
·main()成为c语言的入口函数其实和c语言本身无关,你的代码是被一小段叫做启动代码的程序所调用的,它需要一个叫做main的地方
·操作系统把你的可执行程序装载到内存里,启动运行,然后调用你的main函数
产生一个窗口---->窗口结构
在窗口里画东西---->DC
得到用户的鼠标和键盘动作---->消息循环和消息处理代码
____________________________________________________
自定义数据类型(typedef)
·c语言提供了一个叫做typedef的功能来声明一个已有的数据类型的新名字比如:
typedef int Length;
使得Length成为int类型的别名
·这样,Length这个名字就可以代替int出现在变量定义和参数声明的地方了
Length a,b,len;
Length numbers[10];
____________________________________________________
联合 -union
____________________________________________________
静态本地变量
·在本地变量定义时加上static修饰符就成为静态本地变量
·当函数离开的时候,静态本地变量会继续存在并保持其值
·静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值
·静态本地变量实际上是特殊的全局变量
·它们位于相同的内存区域
·静态本地变量具有全局的生存期,函数内的局部作用域
·static在这里的意思是局部作用域(本地可以访问)
____________________________________________________
*返回指针的函数
·返回本地变量的地址是危险的
·返回全局变量或静态本地变量的地址是安全的
·返回在函数内malloc的内存是安全的,但是容易造成问题
·最好的做法是返回传入的指针
编译预处理指令
·#开头的是编译预处理指令
·它们不是C语言的成分,但是c语言程序离不开它们
·#define用来定义一个宏
·#define<名字><值>
·注意没有结尾的分号,因为不是c的语句
·名字必须是一个单词,值可以是各种东西
·在c语言的编译器开始编译之前,编译预处理程序(cpp)会把程序中的名字换成值
·完全的文本替换
·gcc-save-temps
宏
·如果一个宏的值中有其他的宏的名字,也是会被替换的
·如果一个宏的值超过一行,最后一行之前的行末需要加\
·宏的值后面出现的注释不会被当作宏的值的一部分
预定义的宏
·_LINE_ 源代码行号
·_FILE_ 源代码全路径
·_DATE_ 时间
·_TIME_
·_STDC_
带参数的宏
·#define cube(x)((x)*(x)*(x))
·宏可以带参数
项目
·在Dev C++中新建一个项目,然后把几个源代码文件加入进去
· 对于项目,Dev C++的编译会把一个项目中所有的源代码文件都编译后,链接起来
·有的IDE有分开的编译和构建两个按钮,前者是对单个源代码文件编译,后者是对整个项目做链接
头文件
·把函数原型放到一个头文件(以.h结尾)中,在需要调用这个函数的源代码文件(.c文件)中#include这个头文件,就能让编译器在编译的时候知道函数的原型
自己的头文件用””,系统给的用<>.
·#include不是用来引入库的
·stdio.h里只有printf的原型,printf的代码在另外的地方,某个.lib(Windows)或.a(Unix)中
·现在的c语言编译器默认会引入所有的标准库
·#include<stdio.h>只是为了让编译器知道printf函数的原型
保证你调用时给出的参数值是正确的类型
不对外公开的函数
·在函数前加上static就使得它成为只能在所在的编译单元中被使用的函数
·在全局变量前面加上static就使得它成为只能在所在的编译单元中被使用的全局变量
声明
int i;是变量的定义
extern int i;是变量的声明
格式化的输入输出
%[flags][width][.prec][hil]type
scanf:%[flag]type
文本vs二进制
·Unix喜欢用文本文件来做数据存储和程序配置
·交互式终端的出现使得人们喜欢用文本和计算机”talk”
·Unix的shell提供了一些读写文本的小程序
·Windows喜欢用二级制文件
·DOS是草根文化,并不继承和熟悉Unix文化
·PC刚开始的时候能力有限,DOS的能力更有限,二进制更接近底层
·文本的优势是方便人类读写,而且跨平台
·文本的缺点是程序输入输出要经过格式化,开销大
·二进制的缺点是人类读写困难,而且不跨平台
·int 的大小不一致,大小端的问题.....
·二进制的优点是程序读写快
二级制读写用fread 和fwrite
在文件中定位
·long ftell(FILE*stream);
·int fseek(FILE*stream,long offset, int whence);
·SEEK_SET:从头开始
·SEEK_CUR:从当前位置开始
·SEEK_END:从尾开始(倒过来)
·这样的二进制文件不具有可移植性
·在int为32位的机器上写成的数据文件无法直接在int为64位的机器上正确读出
·解决方案之一是放弃使用int,而是typedef具有明确大小的类型
·更好的方案是用文本
·其实所有的文件最终都是二进制的
·文本文件无非是用最简单的方式可以读写的文件
·more\tail
·cat
·vi
·而二进制文件是需要专门的程序来读写的文件
·文本文件的输入输出是格式化\可能经过转码
按位运算(把整数当二进制进行按位运算)
逻辑运算vs按位运算
·对于逻辑运算,它只看到两个值:0和1
·可以认为逻辑运算相当于把所有非0值都变成1,然后做按位运算
对一个数做两次异或运算则还是原来那个数
左移<<
·i<<j
·i中所有的位向左移动j个位置,而右边填入0
·所有小于int的类型,移位以int的方式来做,结果是int
·x<<1等价于x*=2
·x<<=n等价于x*=
关于结构体类型的定义的总结;
一般格式就是;
struct 结构体名(也就是可选标记名)
{
成员变量;
};//使用分号表示定义结束;
可变数组
链表
链表是一种常见的基础数据结构,也是一个功能极为强大的数组,可以在节点中定义多种数据类型,还可以根据需要随意怎天,删除,插入节点.链表都有一个头指针,一般以head来表示,存放的是一个地址,链表中的节点分为两类,头结点和一般节点,头结点没有数据域,
链表中每个节点分为两部分,一部分是数据域,一部分是指针域.它的地址部分放一个null,链表到此结束
信息安全( 1,传统的网络安全2.云安全3公共安全)
什么是信息安全?保护数据
2017年wannacry病毒席卷全球/撞库/暗网:隐匿性.洋葱路由不可追踪/表层网络,深网,暗网/
熟悉web渗透测试方法和功放技术,包括SQL注入.xss跨站.CSRF伪造请求.命令执行等owasp top10安全漏洞与防御,有一定的漏洞分析和挖掘能力;
安全岗位核心技能需求:
熟悉Linux,Windows不同平台的渗透测试,了解常用web框架.数据库,中间件,和操作系统 弱点以及相关攻防技术;
熟悉国内外主流安全工具包括Kali Linux,metasploit,Nessus,nmap.awvs,burp,appscan等
熟悉一门编程语言,有一定的代码编写能力;
Wireshark
c语言
嵌入式阅读 结合关系一般自左向右,单目+-和赋值=自右向左\这样的表达式太复杂,不容易阅读和理解
#include <stdio.h>
在 C 语言中,sizeof () 是一个判断数据类型或者表达式长度的运算符。
整数类型
char:一字节 8比特
short: 2字节
int:取决于编译器CPU,通常的意义是一个字
long:取决于编译器,通常是两个字
longlong
一台计算器的字长是指当前计算器reg(寄存器)的宽度
寄存器一次可以处理的数据=int 一个字
int就是用来表达寄存器的
如何表示负数
二进制负数
一个字节可以表达的数:
00000000-11111111(0-255)
三种方案:
1,仿照十进制,有一个特殊的标志表示负数
- 取中间的数为0,如1000000表示0,比它小的是负数,比它大的是正数
- 补码
补码:对于-a,其补码就是0-a,实际上是2的n次方-a,n是这种类型的位数
补码的意义就是拿补码和原码可以加出一个溢出的”零”
整数的输入输出
只有两种形式:int或long long
%d:int
%u:unsigned
%ld:long long
%lu:unsigned long long
一个以0开始的数字字面量是8进制
一个以0x开始的数字字面量是16进制
16进制很适合表达二进制数据,因为4位二进制正好是一个16进制位
8进制的一位数字正好表达3位二进制
因为早期计算机的字长是12的倍数,而非8
带小数点的字面量是double而非float
float需要用f或F后缀来表明身份
*浮点数内部表达
浮点数在计算时是由专用的硬件部分实现的
计算double和float所用的部件是一样的
^字符类型
char是一种整数,也是一种特殊的类型:字符.这是因为:
用单引号表示的字符字面量:’a’
‘’也是一个字符
printf和scanf里用%c来输入输出字符
*逃逸字符
用来表达无法印出来的控制字符或特殊字符,它由一个反斜杠”\”开头,后面跟上另一个字符,这两个字符合起来,组成了一个字符
不同的shell做出不同的反应
||优先级大于&&
count[x]++;
*取地址运算
sizeof
是一个运算符,给出某个类型或变量在内存中所占据的字节数
运算符&
·scanf(“%d”,&i);里的&
·获得变量的地址,他的操作数必须是变量
指针
就是保存地址的变量
int*P=&i
变量的值是内存的地址
·普通变量的值是实际的值
·指针变量的值是具有实际值的变量的地址
访问那个地址上的变量*
* 是一个单目运算符,用来访问指针的值所表示的地址上的变量
·可以做右值也可以做左值(可以放等号两边)
·int k=*p;
·*p=k+1
^指针的使用
一
·交换两个变量的值
二
·函数返回多个值,某些值就只能通过指针返回
·传入的参数实际上是需要保存带回的结果的变量
函数返回运算的状态,结果通过指针返回
常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错:
-1或0
但是当任何数值都是有效的可能结果是,就得分开返回了
·后续的语言(c++,java)采用了异常机制来解决这个问题
*指针最常见的错误
·定义了指针变量,还没有指向任何变量,就开始使用指针
传入函数的数组成了什么?
·函数参数表中的数组实际上是指针
·sizeof(a)==sizeof(int*)
·但是可以用数组的运算符[]进行运算
数组变量是特殊的指针
·数组变量本身表达地址所以
·int a[10];int*p=a;//无需用&取地址
·但是数组的单元表达的是变量,需要用&取地址
·a==&a[0]
·[]运算符可以对数组做,也可以对指针做:
·数组变量是const的指针,所以不能被赋值
·int a[]<==>int *const a=.....
C语言const的用法详解,C语言常量定义详解 (biancheng.net)
判断哪个被const了的标志是const在*的前面还是后面
^保护数组值
·因为把数组传入函数时,传递的是地址,所以那个函数内部可以修改数组的值
·为了保护数组不被函数破坏,可以设置参数为const
·int sum (const int a[],int length);
·给一个指针加1表示要让指针指向下一个变量
int a[10];
int*p=a;
*(p+1)-->a[1]
·如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义
*p++
·取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
·*的优先级虽然高,但是没有++高
·常用于数组类的连续空间操作
·在某些CPU上,这可以直接被翻译成一条汇编指令
0地址
·当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
·所以你的指针不应该具有0值
·因此可以用0地址来表示特殊的事情
·返回的指针是无效的
·指针没有被真正初始化(先初始化为0)
·NULL是一个预定定义的符号,表示0地址
·有的编译器不愿意用0来表示0地址
指针的类型
·无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
·但是指向不同类型的指针是不能直接互相赋值的
·这是为了避免用错指针
指针类型转换
·void*表示不知道指向什么东西的指针
·计算时与char*相同(但不相通)
·指针也可以转换类型
·int*p=&i’;void*q=(void*)p;
·这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量
·我不再当你是int,我认为你就是个void!
size_t类型表示c中任何对象所能达到的最大长度,它是无符号整数.
字符串
·以0(整数0)结尾的一串字符
·0或’\0’是一样的,但是和’0’不同
·0标志字符串的结束,但它不是字符串的一部分
·计算字符串长度的时候不包含这个0
·字符串以数组的形式存在,以数组或指针的形式访问
·更多的是以指针的形式
·string.h里有很多处理字符串的函数
字符串变量
·char*str=”hello”;
·char word[]=”Hello”;
·char line[10]=”Hello”;
·Hello会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0
·两个相邻的字符串常量会被自动连接起来
·C语言的字符串是以字符数组的形态存在的
·不能用运算符对字符串做运算
·通过数组的方式可以遍历字符串
·唯一特殊的地方是字符串字面量可以用来初始化字符数组
%*c表示跳过这个字符
%作为引导符,控制输入输出的格式.
%s表示输出字符串形式
%f表示输出为浮点型数据
%o表示以8进制形式输出整数
%d表示输出整形数据
%ld表示输出为长整型数据
%u表示输出格式为无符号数据
%c表示输出字符数据(char类型)
%md中m指定输出字段的宽度
%x表示以16进制形式输出整数
如果需要修改字符串,应该用数组
char*中只有它所指的字符数组有结尾的0,才能说它所指的是字符串
字符串输如输出
·scanf不安全,
若要可以在%和s间加数字例如%7s,限制读取的位数
程序参数
·int main(int argc,char const*argv[])
·argv[0]是命令本身
·当使用Unix的符号链接时,反映符号链接的名字
处理字符串需要用
string.h
·strlen<·size_t strlen(const char*s)
·返回s的字符串长度(不包括结尾的0)>
·strcmp<·两个字符串作比较,返回值是两个不相等的字符的差值>
·strcpy< ·char*strcpy(char*restrict dst, const char*restrict stc);
·把src的字符串拷贝到dst
·restrict表明src和dst不重叠(C99)
·返回dst
·为了能链起代码来>
·strcat<char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾>
·strchr<字符串搜索函数(字符串中找字符)
·char*strchr(const char*s,int c);
·char*strrchr(const char*s, int c);
·返回NULL表示没有找到
·strstr
枚举
·枚举是一种用户定义的数据类型,它用关键字enum以如下语法来声明:enum枚举类型名字{名字0,...名字n}
·枚举类型名字通常并不真的使用,要用的是在大括号里的名字,因为他们就是常量符号,它们的类型是int,值则依次从0到n.如:enum colors{red,yellow,green};
枚举比宏好,因为枚举有int类型
结构类型
声明结构的形式
①struct point{
int x;
int y;
};
struct point p1,p2;
p1和p2都是point里面有x和y的值
②struct{
int x;
int y;
}p1,p2;
p1和p2都是一种无名结构,里面有x和y
③struct point{
int x;
int y;
}p1,p2;
p1和p2都是point里面有x和y的值t
对于第一和第三种形式,都声明了结构point.但是第二种形式没有声明point,只是定义了两个变量.
结构成员
·结构和数组有点像
·数组用[]运算符和下标访问其成员
·a[0]=10;
·结构用.运算符和名字访问其成员
·today.day
·student.firstName
·pl.x
·pl.y
结构运算
·要访问整个结构,直接用结构变量的名字
·对于整个结构,可以做赋值\取地址,也可以传递给函数参数
·pl=(struct point){5,10};//相当于pl.x=5;
pl.y=10
·pl=p2;//相当于pl.x=p2.x;pl.y=p2.y;
结构指针
·和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符
·struct date*pDate=&today;
____________________________________________________
结构作为函数参数
int numberOfDays(struct date d)
·整个结构可以作为参数的值传入函数
·这时候是在函数内新建一个结构变量,并复制调用者的结构的值
·也可以返回一个结构
·这与数组完全不同
如果有变量定义:
struct rectangle r,*rp;
rp=&r;
那么下面的四种形式是等价的:
r.ptl.x
rp->ptl.x
(r.ptl).x
(rp->ptl).x
但是没有rp->ptl->x
ACLLib Windows API
·main()成为c语言的入口函数其实和c语言本身无关,你的代码是被一小段叫做启动代码的程序所调用的,它需要一个叫做main的地方
·操作系统把你的可执行程序装载到内存里,启动运行,然后调用你的main函数
产生一个窗口---->窗口结构
在窗口里画东西---->DC
得到用户的鼠标和键盘动作---->消息循环和消息处理代码
____________________________________________________
自定义数据类型(typedef)
·c语言提供了一个叫做typedef的功能来声明一个已有的数据类型的新名字比如:
typedef int Length;
使得Length成为int类型的别名
·这样,Length这个名字就可以代替int出现在变量定义和参数声明的地方了
Length a,b,len;
Length numbers[10];
____________________________________________________
联合 -union
____________________________________________________
静态本地变量
·在本地变量定义时加上static修饰符就成为静态本地变量
·当函数离开的时候,静态本地变量会继续存在并保持其值
·静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值
·静态本地变量实际上是特殊的全局变量
·它们位于相同的内存区域
·静态本地变量具有全局的生存期,函数内的局部作用域
·static在这里的意思是局部作用域(本地可以访问)
____________________________________________________
*返回指针的函数
·返回本地变量的地址是危险的
·返回全局变量或静态本地变量的地址是安全的
·返回在函数内malloc的内存是安全的,但是容易造成问题
·最好的做法是返回传入的指针
编译预处理指令
·#开头的是编译预处理指令
·它们不是C语言的成分,但是c语言程序离不开它们
·#define用来定义一个宏
·#define<名字><值>
·注意没有结尾的分号,因为不是c的语句
·名字必须是一个单词,值可以是各种东西
·在c语言的编译器开始编译之前,编译预处理程序(cpp)会把程序中的名字换成值
·完全的文本替换
·gcc-save-temps
宏
·如果一个宏的值中有其他的宏的名字,也是会被替换的
·如果一个宏的值超过一行,最后一行之前的行末需要加\
·宏的值后面出现的注释不会被当作宏的值的一部分
预定义的宏
·_LINE_ 源代码行号
·_FILE_ 源代码全路径
·_DATE_ 时间
·_TIME_
·_STDC_
带参数的宏
·#define cube(x)((x)*(x)*(x))
·宏可以带参数
项目
·在Dev C++中新建一个项目,然后把几个源代码文件加入进去
· 对于项目,Dev C++的编译会把一个项目中所有的源代码文件都编译后,链接起来
·有的IDE有分开的编译和构建两个按钮,前者是对单个源代码文件编译,后者是对整个项目做链接
头文件
·把函数原型放到一个头文件(以.h结尾)中,在需要调用这个函数的源代码文件(.c文件)中#include这个头文件,就能让编译器在编译的时候知道函数的原型
自己的头文件用””,系统给的用<>.
·#include不是用来引入库的
·stdio.h里只有printf的原型,printf的代码在另外的地方,某个.lib(Windows)或.a(Unix)中
·现在的c语言编译器默认会引入所有的标准库
·#include<stdio.h>只是为了让编译器知道printf函数的原型
保证你调用时给出的参数值是正确的类型
不对外公开的函数
·在函数前加上static就使得它成为只能在所在的编译单元中被使用的函数
·在全局变量前面加上static就使得它成为只能在所在的编译单元中被使用的全局变量
声明
int i;是变量的定义
extern int i;是变量的声明
格式化的输入输出
%[flags][width][.prec][hil]type
scanf:%[flag]type
文本vs二进制
·Unix喜欢用文本文件来做数据存储和程序配置
·交互式终端的出现使得人们喜欢用文本和计算机”talk”
·Unix的shell提供了一些读写文本的小程序
·Windows喜欢用二级制文件
·DOS是草根文化,并不继承和熟悉Unix文化
·PC刚开始的时候能力有限,DOS的能力更有限,二进制更接近底层
·文本的优势是方便人类读写,而且跨平台
·文本的缺点是程序输入输出要经过格式化,开销大
·二进制的缺点是人类读写困难,而且不跨平台
·int 的大小不一致,大小端的问题.....
·二进制的优点是程序读写快
二级制读写用fread 和fwrite
在文件中定位
·long ftell(FILE*stream);
·int fseek(FILE*stream,long offset, int whence);
·SEEK_SET:从头开始
·SEEK_CUR:从当前位置开始
·SEEK_END:从尾开始(倒过来)
·这样的二进制文件不具有可移植性
·在int为32位的机器上写成的数据文件无法直接在int为64位的机器上正确读出
·解决方案之一是放弃使用int,而是typedef具有明确大小的类型
·更好的方案是用文本
·其实所有的文件最终都是二进制的
·文本文件无非是用最简单的方式可以读写的文件
·more\tail
·cat
·vi
·而二进制文件是需要专门的程序来读写的文件
·文本文件的输入输出是格式化\可能经过转码
按位运算(把整数当二进制进行按位运算)
逻辑运算vs按位运算
·对于逻辑运算,它只看到两个值:0和1
·可以认为逻辑运算相当于把所有非0值都变成1,然后做按位运算
对一个数做两次异或运算则还是原来那个数
左移<<
·i<<j
·i中所有的位向左移动j个位置,而右边填入0
·所有小于int的类型,移位以int的方式来做,结果是int
·x<<1等价于x*=2
·x<<=n等价于x*=
关于结构体类型的定义的总结;
一般格式就是;
struct 结构体名(也就是可选标记名)
{
成员变量;
};//使用分号表示定义结束;
可变数组
链表
链表是一种常见的基础数据结构,也是一个功能极为强大的数组,可以在节点中定义多种数据类型,还可以根据需要随意怎天,删除,插入节点.链表都有一个头指针,一般以head来表示,存放的是一个地址,链表中的节点分为两类,头结点和一般节点,头结点没有数据域,
链表中每个节点分为两部分,一部分是数据域,一部分是指针域.它的地址部分放一个null,链表到此结束
};//使用分号表示定义结束;
可变数组
链表
链表是一种常见的基础数据结构,也是一个功能极为强大的数组,可以在节点中定义多种数据类型,还可以根据需要随意怎天,删除,插入节点.链表都有一个头指针,一般以head来表示,存放的是一个地址,链表中的节点分为两类,头结点和一般节点,头结点没有数据域,
链表中每个节点分为两部分,一部分是数据域,一部分是指针域.它的地址部分放一个null,链表到此结束