Ruby3 流程控制

if/else/elsif

1
2
3
4
5
6
7
8
9
a = "hello"
b = false
if a
    p a
elsif b
    p b
else
    p "ok"
end

unless

unless相当于if的反向断言

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    unless false
        "ok"
    end
    # => "ok"

## if/unless

    a = 1 if a != 1 #如果a不是1 则a复制为1
    b = 2 unless defined?(b) #如果b未定义 那么就定义b赋值为2

## case

    case a
    when 1
        1
    when /hello/
        "hello world"
    when Array
        []
    else
        "ok"
    end

## while

    a = 0
    while a < 100 do
        p a
        a += 1
    end

ruby 没有++和–操作符

Ruby1 数据类型,变量

  1. 整数类型: 3,222
  2. 小数: 3.14
  3. 字符串: hello,world
  4. 布尔类型: true(TrueClass),false(FalseClass)
  5. 数组: [1,2],["hello","hello world"]
  6. Hash(字典): {"name"=>"luo","age"=>24},{:name=>"daoyi",:age=>24}
  7. Symbol(符号): :a,:hello,:"hello world"
  8. Range: 1..10,1...10(三个点不包括10本身)
  9. 正则: /hello/i

String 字符串

1
2
3
4
5
a = "hello" #=> hello
b = "world" #=> world
a.class #=> String
a + " " + b #=> hello world
"#{a} #{b}" #=> hello world

string method

1
2
3
"hello world".length #=>11
"hello world".capitalize #=> Hello world
"hello world".gsub("world","gril").upcase #=> HELLO GIRL

变量赋值

1
2
3
4
5
6
a = "hello" # => hello
a.object_id # => 70353313681980
a.replace("hello2") # => "hello2"
a.object_id # => 70353313681980
a = "hello"
a.object_id # => 70353313549380

当a 使用replace时候仍然是原本的引用地址,所以Object_id不变
但是当a重新赋值为hello 的时候,a的引用地址发生了变化 object_id就改变了

Ruby2 Method and Block

Method(Function)

1
2
3
4
5
6
7
8
9
def hi(name)
    p "hi " + name
end
hi("666") # => "hi 666"
hi "code" #括号省略  => "hi code"
def hello name
    p "hello #{name}"
end
hello "world" # => "hello world"

Method 参数

1
2
3
4
5
6
7
8
9
def hi name="code"
    p "hi " +name
end
hi # => "hi code"
def hi name,age=32
    p "#{name} is #{age}"
end
hi "code" # => "code is 32"
hi "code", 54 # => "code is 54"

Method 返回值

凡有方法都有返回值,方法体最后一行代码的返回值默认会做为方法的返回值,也可以显式的使用return关键字

Cpp7 C++的多态实现 — 虚表

多态的实现原理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include "stdafx.h"
#include
#include
class A
{
public:
    int x;
    virtual void Test()
    {
        printf("A \n");
    }
protected:
private:
};
class B:public A
{
public:
    int x;
    void Test()
    {
        printf("B \n");
    }
protected:
private:
};
void Fun(A* p)
{
    p->Test();
}
int main(int argc, char* argv[])
{
    A a;
    B b;
    Fun(&b);
    return 0;
}
//我们发现在这里 调用的test函数 是b的 因为fun方法传入的对象是b b继承自a 这里体现了多态
//反编译
31:   void Fun(A* p)
32:   {
00401050   push        ebp
00401051   mov         ebp,esp
00401053   sub         esp,40h
00401056   push        ebx
00401057   push        esi
00401058   push        edi
00401059   lea         edi,[ebp-40h]
0040105C   mov         ecx,10h
00401061   mov         eax,0CCCCCCCCh
00401066   rep stos    dword ptr [edi]
33:       p->Test();
00401068   mov         eax,dword ptr [ebp+8]
0040106B   mov         edx,dword ptr [eax]
0040106D   mov         esi,esp
0040106F   mov         ecx,dword ptr [ebp+8]
00401072   call        dword ptr [edx]    //间接调用 + 虚表
00401074   cmp         esi,esp
00401076   call        __chkesp (00401240)
34:   }
@ILT+0(?Fun@@YAXPAVA@@@Z):
00401005   jmp         Fun (00401050)
@ILT+5(??0B@@QAE@XZ):
0040100A   jmp         B::B (00401190)
@ILT+10(??0A@@QAE@XZ):
0040100F   jmp         A::A (00401100)
@ILT+15(?Test@B@@UAEXXZ):
00401014   jmp         B::Test (004011f0)
@ILT+20(?Test@A@@UAEXXZ):
00401019   jmp         A::Test (00401140)
@ILT+25(_main):
0040101E   jmp         main (004010a0)

总结:

Cpp6 封装、继承和多态

继承

  1. 子类从父类继承成员变量
  2. 子类从父类继承成员函数
#include "stdafx.h"
class Person
{
public:
    int Age;
    int Sex;
    void Word()
    {
        printf("Person:Work");
    }
};
class Teacher:public Person
{
public:
    int Level;
};
int main()
{
    Teacher t;
    t.Age = -1; //合法但是不合理
    t.Sex = 2;
    t.Level = 3;
    return 0;
}

实现数据隐藏

为什么要隐藏数据成员

kali rolling 国内源

#阿里云kali源
deb http://mirrors.aliyun.com/kali kali-rolling main non-free contrib
deb-src http://mirrors.aliyun.com/kali kali-rolling main non-free contrib
deb http://mirrors.aliyun.com/kali-security kali-rolling/updates main contrib non-free
deb-src http://mirrors.aliyun.com/kali-security kali-rolling/updates main contrib non-free
#中科大kali源
deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib
deb-src http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib
deb http://mirrors.ustc.edu.cn/kali-security kali-current/updates main contrib non-free
deb-src http://mirrors.ustc.edu.cn/kali-security kali-current/updates main contrib non-free

Cpp5 在堆中创建对象和引用类型

我们可以在什么地方创建对象?

  1. 全局变量区

    Person p;

  2. void Max() { Person p; }

  3. newdelete

    //在堆中创建对象: Person* p = new Person(); //释放对象占用的内存 delete p;

Cpp4 类成员的访问控制

好的编程习惯 -定义和实现分开

  1. 代码会有更好的可读性
  2. 但不是必须的

在头文件中只留下声明代码

Test.h

struct sclass
{
    int x;
    int y;
    int Bigger(int x,int y);
    int Max(int x,int y,int z);
};

Test.cpp

Cpp3 继承

什么是继承

struct Person 
{ 
  int age; 
  int sex; 
};
struct Teacher 
{ 
  int age; 
  int sex; 
  int level; 
  int classId; 
};
struct Teacher:Person 
{ 
  int level; 
  int classId; 
};

总结:

1、什么是继承?
继承就是数据的复制
2、为什么要用继承?
减少重复代码的编写
3、Person 称为父类或者基类

Cpp2 构造函数和析构函数

什么是构造函数

#include "stdafx.h"
#include
struct Sclass {
    int a;
    int b;
    int c;
    int d;
    Sclass()//构造函数
    {
        printf("观察这个函数 \n");
    }
    Sclass(int a,int b,int c,int d)//构造函数
    {
        this->a=a;
        this->b=b;
        this->c=c;
        this->d=d;
        printf("观察这个函数 2\n");
    }
    int Plus()
    {
        return a+b+c+d;
    }
};
int main(int argc, char* argv[])
{
    Sclass s;
    Sclass s2(1,2,3,4);
    return 0;
}
//反汇编:
Sclass s;
0040D408   lea         ecx,[ebp-10h]
0040D40B   call        @ILT+5(Sclass::Sclass) (0040100a)
Sclass s2(1,2,3,4);
0040D770   push        4
0040D772   push        3
0040D774   push        2
0040D776   push        1
0040D778   lea         ecx,[ebp-20h]
0040D77B   call        @ILT+10(Sclass::Sclass) (0040100f)
//观察发现,分配一个对象,构造函数 方法直接就会被调用

总结特点:

链表

实现思想 :通过指针把要元素串起来

简单结构图

struct Student
{
    char name[10];
    int score;
}
struct Node
{
    Student Element;
    Node* next;
}

每一个节点包含此节点信息和下一个节点的指针

Cpp1 封装和this指针

封装

C语言和C++语言的区别

C++是对C的补充扩展,C原有的语法C++都支持,并在此基础上扩展了一些新的语法:
继承、封装、多态、模板等等

C语言22 调用约定和函数指针

函数调用约定

就是告诉编译器:怎么传递参数,怎么传递返回值,怎么平衡堆栈

int method(int x,int y)
{
    return x+y;
}
//调用
method(1,2);

常见的几种调用约定:

调用约定参数压栈顺序平衡堆栈
__cdecl从右至左入栈调用者清理栈
__stdcall从右至左入栈自身清理堆栈
__fastcallECX/EDX 传送前两个,剩下:从右至左入栈自身清理堆栈
int __stdcall method(int x,int y)
{
    return x+y;
}
//调用
method(1,2);
//观察反汇编堆栈变化

PS:一般情况下自带库默认使用 __stdcall 我们写的代码默认使用 __cdecl

C语言21 指针数组与数组指针

指针数组的定义

char arr[10]; //10 char
char* arr[10]; //10 指针(char*)
Point* arr[10]; //10 指针(Point*)
int******** arr[10]; 

指针数组的赋值

char* a = "Hello";
char* b = "World";
//方式1:
char* arr[2]= {a,b};
//方式2:
char* arr[2];
arr[0]=a;
arr[1]=b;

结构体指针数组

struct Point
{
    int x;
    int y;
}
Point p;            //8字节 一个结构体
Point arr[10];      //8字节 * 10 结构体数组
Point* arrPoint[10];  //4字节 * 10 结构体指针数组

分析下面的代码

int arr[] = {1,2,3,4,5,6,7,8,9,0};
int* p =&arr[0]; //数组首个元素地址
int* p = arr;    //数组首个元素地址 跟上面的表达方式是一样的 也可以说是简写形式
int* p =&arr;    //什么情况? --编译失败 (int(*)[10] 类型 不可以转换为 int* 类型)
//我们使用显式声明类型来躲避编译器检查
int* x = (int*)&arr;
//我们编译后发现 此时的x 值和上面的p值是一样的
//那么区别是什么呢
//当我们使用&arr 获得的类型就是 int(*)[10] 类型 也就是数组指针类型

数组指针的定义

//定义数组指针的写法
int(*px)[5];//一维数组指针  px 这里就是当前指针的名字
char(*px)[3];  //px 就是指针的名字
int(*px)[2][2]; //二维数组指针  px 就是指针的名字
char(*px)[3][3][3]; //三维数组指针  px 就是指针的名字
//思考:
//int *p[5] 与 int(*p)[5] 有什么区别?

数组指针的宽度与赋值

int(*px1)p[5];      //一维数组指针
char(*px2)[3];
int(*px3)[2][2];        //二维数组指针
char(*px4)[3][3][3];    //三维数组指针
printf("%d %d %d %d\n",sizeof(px1),sizeof(px2),sizeof(px3),sizeof(px4));
// 4 4 4 4
px1 = (int (*)[5])1;
px2 = (char (*)[3])2;
px3 = (int (*)[2][2])3;
px4 = (char (*)[3][3][3])4;

数组指针的运算

int(*px1)p[5];      //一维数组指针
char(*px2)[3];
int(*px3)[2][2];        //二维数组指针
char(*px4)[3][3][3];    //三维数组指针
px1 = (int (*)[5])1;
px2 = (char (*)[3])1;
px3 = (int (*)[2][2])1;
px4 = (char (*)[3][3][3])1;
px1++;  //int(4) *5  +20 =21
px2++;  //char(1) *3   +3 =4
px3++;  //int(4) *2 *2  +16 =17
px4++;  //char(1) *3 *3 *3 +9 =10
printf("%d %d %d %d \n",px1,px2,px3,px4);

数组指针的使用

//第一种:
int arr[] = {1,2,3,4,5,6,7,8,9,0}
int(*px)[10]=&arr
//*px 是啥类型? => int[10] 数组类型
//px[0] 等价于 *px 所以 *px 也等于 int[10]数组
printf("%d %d \n",(*px)[0],px[0][0]);
px++; //后 (*px)[0]就访问整个数组地址后的地址内的数据
//第二种:
int arr[3][3] = {
    {1,2,3},
    {4,5,6},
    {7,8,9}
};
//此时的 px指针 指向的 {1,2,3}这个数组的首地址
int(*px)[3] = &arr[0];
// *px -> 此时就是数组{1,2,3}本身
//越过第一个数组 此时px指针指向 {4,5,6}的首地址
px++;
printf("%d %d \n",(*px)[0],px[0][0]);
//这里打印的就是 4 4

二维数组指针可以访问一维数组吗?

int arr[] = {1,2,3,4,5,6,7,8,9,0};
int(*px)[2][2] = (int(*)[2][2])arr;
//思考:
// *px 是什么类型
//int[2][2] 二维数组
//(*px)[1][1]的值是什么? px[0][1][1]的值是什么
//4

C语言20 结构体指针

探测结构体指针的特性

struct Point
{
    int x;
    int y;
}
  1. ++、–
  2. 加法与减法
  3. 指针相减

使用结构体指针

//创建结构体
Point p;
p.x=10;
p.y=20;
//声明结构体指针
Point* ps;
//为结构体指针赋值
ps = &p;
//通过指针读取数据
printf("%d \n",ps->x);
//通过指针修改数据
ps->y=100;
printf("%d\n",ps=>y);

结构体指针一定要指向结构体吗?

struct Point
{
    int x;
    int y;
}
int arr[10]={1,2,3,4,5,6,7,8,9,10}
Point* p = (Point*)arr;
for(int i = 0;i < 5;i++,p++)
{
    printf("%d %d\n",p->x,p->y);
}

C语言19 指针取值的两种方式

一级指针和多级指针

int i =100;
int* p1 = &i; 
int** p2 = &p1; 
int*** p3 = &p2; 
int**** p4 = &p3; 
int***** p5 = &p4 
int****** p6 = &p5; 
int******* p7 = &p6;
int****** px1 = *(p7);
int***** px2 = *(px1);
int**** px3 = *(px2);
int*** px4 = *(px3);
int** px5 = *(px4);
int* px6 = *(px5);
int px7 = *(px6);
int px7 = *(*(*(*(*(*(*(p7))))))); 

*()与[]的互换表示

int* p = (int*)1;
printf("%d %d \n",p[0],*p);  //p[0] = *(p+0) = *p 
int** p = (int**)1;
printf("%d %d \n",p[0][0],**p); 
printf("%d %d \n",p[1][2],*(*(p+1)+2));
int*** p = (int***)1;
printf("%d %d \n",p[1][2][3],*(*(*(p+1)+2)+3)); 
同理:
*(*(*(*(*(*(*(p7)))))))) 
= *(*(*(*(*(*(p7+0)+0)+0)+0)+0)+0)
= p7[0][0][0][0][0][0][0] 

总结

*(p+i) = p[i] 
*(*(p+i)+k) = p[i][k] 
*(*(*(p+i)+k)+m) = p[i][k][m] 
*(*(*(*(*(p+i)+k)+m)+w)+t) = p[i][k][m][w][t] 
*() 与 []可以相互转换 

C语言18 指针与字符串

字符串的几种表示方式有什么区别?

char str[6] = {"A","B","C","D","E","F"}; //"\0" 或者0 --堆栈中 局部变量
char str[] = "ABCDE";                   //编译器末尾填0 常量区
char* str= "ABCDE";                     //常量区
//打印
printf("%s \n",str);
//字符串打印时 遇到 0 才会停止

常用的字符串函数

1、int strlen(char* s)
返回值是字符串S的长度,不包括结束符"/0"
弊端:字符串中英文混合的话。返回的长度有问题,因为中文占用两个字节
2、char* strcpy(char* dest, char* src)
复制字符串src到dest中。返回指针为dest的值
3、char* strcat(char* dest, char* src)
将字符串src添加到dest尾部。返回指针为dest的值
4、int strcmp(char* s1, char* s2)
字符串对比,一样返回0 不一样返回非0

指针函数

本质就是函数,只不过函数的返回类型是某一类型的指针
如:

C语言17 数组参数传递

基本类型参数传递

int x =1;
void plus(int p)
{
    p = p+1;
}
printf("%d \n",x);
//x的值是多少?

数组作为参数

编写一个函数,能够打印任意整形数组的值

C语言16 取值运算符

“*”的几种用途

  1. 乘法运算符

    int x = 1; int y = 2; int z = x * y;

  2. 定义新的类型

    char x; char* x;

  3. 取值运算符

      • 指针类型的变量 int* a =(int*)1; printf("%x \n",(a +1)); 10: int a =(int*)1; 00401028 mov dword ptr [ebp-4],1 11: printf("%x \n",*(a +1)); 0040102F mov eax,dword ptr [ebp-4] 00401032 mov ecx,dword ptr [eax+4] 00401035 push ecx 00401036 push offset string "%x \n" (0042201c) 0040103B call printf (00401070) 00401040 add esp,8 当然这里运行的时候肯定出错 因为 1 是一个异常的不存在的地址

探测 * 指针类型 的类型

int*** a;
int***** b;
int******* c;
int* d;
//int x = *(a+1);
//int x = *(b++);
//int x = *(c-5);
//int x = *(d+6);
//总结
//    *加指针类型 的类型是 指针类型减去一个*

取值运算符举例

int x=1;                        int x =1;
int* p = &x;                    int* p = &x;
printf("%x %x \n",p,*(p));      int** p2 = &p;
*(p)=2;                         int*** p3 = &p2;
printf("%d \n",x);              int r = *(*(*(p3)));
                                printf("%d \n",r);