Ruby6 细节补充
代码规范
- 使用UTF-8编码
- 使用空格缩进,不使用tab, 1 tab = 2 spaces
- 不需要使用分号(;)和反斜杠()连接代码
Demo
Demo
# 变量交换
a = 1
b = 2
b,a = a,b
puts a
puts b
puts "-" * 30
x = [1, 2, 3]
a, b = x #默认会把数组中的值依次赋值给 a ,b
puts a
puts b
puts "-" * 30
x = [1, 2, 3]
a, *b = x #这里a会接受第一个元素 b用了*号 表示接受剩下所有的元素
puts a
p b
#output
2
1
------------------------------
1
2
------------------------------
1
[2, 3]
# number
puts 1 / 10
puts 1 / 10.0
puts "-" * 30
#output
0.1
------------------------------
# string
a = "world"
b = %Q{
hello
#{a}
}
# 这里不但可以用 {} 也可以用 ()
# 但是这里的Q必须是大Q 如果是小q的话 就相当于单引号的效果
puts b
puts "-" * 30
{puts "hello"}
Demo:
# block usage
def hello
puts "hello method start"
yield
yield
puts "hello method end"
end
hello {puts "i am in block"}
#output
hello method start
i am in block
i am in block
hello method end
# yield with parameter
def hello
puts "hello method start"
yield("hello","world")
puts "hello method end"
end
hello {|x,y| puts "i am in block,#{x} #{y}"}
#output
hello method start
i am in block,hello world
hello method end
# yield with paramter
def hello name
puts "hello method start"
result = "hello " + name
yield(result)
puts "hello method end"
end
hello("world"){|x| puts "i am in block,i got #{x}"}
#output
hello method start
i am in block,i got hello world
hello method end
# build in methods
["cat", "dog","frog"].each do |animal|
puts animal
end
puts "-" * 30
["cat","dog","frog"].each{|animal| puts animal}
#output
cat
dog
frog
------------------------------
cat
dog
frog
# build in methods
10.times do |t|
puts t
end
puts "-" * 30
("a".."d").each { |char| puts char}
#output
1
2
3
4
5
6
7
8
9
------------------------------
a
b
c
d
# varibale scope
# before ruby2.0
x = 1
[1, 2, 3].each { |x| puts x}
puts x # => x will be 3,which is incorrect
#output
1
2
3
1
如果是在ruby2之前的版本 那么外部的变量x会被改变
# varibale scope
# 如果是2.0版本之后 puts x会报错
sum = 0
[1, 2, 3].each { |x| sum += x}
puts sum
# puts x
#output
6
# block return value
class Array
def find
each do |value|
return value if yield (value)
end
nil
end
end
puts [1, 2, 3].find { |x| x == 2 }
#output
2
# block as named parameter
def hello name, &block
puts "hello #{name}, from method"
block.call(name)
end
hello("world") {|x| puts "hello #{x} form block"}
#output
hello world, from method
hello world form block
# yield with parameter
def hello
puts "hello method start"
yield("hello","world")
puts "hello method end"
end
hello {|x,y| puts "i am block ,#{x},#{y}"}
#output
hello method start
i am block ,hello,world
hello method end
# block_given?
def hello
if block_given?
yield
else
puts "hello from method"
end
end
hello
puts "-" * 30
hello {puts "hello from block"}
#output
hello from method
------------------------------
hello from block
# block can be closure
def counter
sum = 0
# 代码库接收了一个参数x 如果x没有定义那么x为1 然后 sum +=x
proc {|x| x = 1 unless x; sum +=x }
end
c2 = counter
puts c2.call(1) #1
puts c2.call(2)
puts c2.call(3)
# 这里 closure 为闭包
#
#output
1
3
6
# new method to create block
# name is required
hello = -> (name){"hello #{name}"}
puts hello.call("world")
puts "-" * 30
# name is required
hello3 = lambda {|name| "hello #{name}"}
puts hello3.call("world")
puts "-" * 30
hello2 = proc {|name| "hello #{name}"}
puts hello2.call
puts hello2.call("world")
# lambda和proc区别 proc可以不传参数 lambda 更像是一个方法,必须传递参数
#output
hello world
------------------------------
hello world
------------------------------
hello
hello world
All Exception inherited from Exception Class
| |
unless相当于if的反向断言
| |
ruby 没有++和–操作符
3,2223.14hello,worldtrue(TrueClass),false(FalseClass)[1,2],["hello","hello world"]{"name"=>"luo","age"=>24},{:name=>"daoyi",:age=>24}:a,:hello,:"hello world"1..10,1...10(三个点不包括10本身)/hello/i | |
| |
| |
当a 使用replace时候仍然是原本的引用地址,所以Object_id不变
但是当a重新赋值为hello 的时候,a的引用地址发生了变化 object_id就改变了
| |
| |
凡有方法都有返回值,方法体最后一行代码的返回值默认会做为方法的返回值,也可以显式的使用return关键字
| |
总结:
#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源
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
全局变量区
Person p;
栈
void Max() { Person p; }
堆 new 和 delete
//在堆中创建对象: Person* p = new Person(); //释放对象占用的内存 delete p;
在头文件中只留下声明代码
Test.h
struct sclass
{
int x;
int y;
int Bigger(int x,int y);
int Max(int x,int y,int z);
};
Test.cpp
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 称为父类或者基类
#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;
}

每一个节点包含此节点信息和下一个节点的指针
C++是对C的补充扩展,C原有的语法C++都支持,并在此基础上扩展了一些新的语法:
继承、封装、多态、模板等等
就是告诉编译器:怎么传递参数,怎么传递返回值,怎么平衡堆栈
int method(int x,int y)
{
return x+y;
}
//调用
method(1,2);
| 调用约定 | 参数压栈顺序 | 平衡堆栈 |
|---|---|---|
| __cdecl | 从右至左入栈 | 调用者清理栈 |
| __stdcall | 从右至左入栈 | 自身清理堆栈 |
| __fastcall | ECX/EDX 传送前两个,剩下:从右至左入栈 | 自身清理堆栈 |
int __stdcall method(int x,int y)
{
return x+y;
}
//调用
method(1,2);
//观察反汇编堆栈变化
PS:一般情况下自带库默认使用 __stdcall 我们写的代码默认使用 __cdecl
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
struct Point
{
int x;
int y;
}
//创建结构体
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);
}
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]
*() 与 []可以相互转换