汇编 函数
进入函数在汇编中其实就是callq,出函数就是retq。
而callq和retq的功能之前已经提到过
参数的传递在x86-64位系统中,有六个寄存器负责传入参数,分别是rdi,rsi,rdx,rcx,r8,r9,同时程序返回时返回值放在rax中。如果大于6个,多的部分就要放在栈上。如果我们想操作在栈上的参数,就要通过 x(%rsp)来操作了。
例如有7个参数,此时第7个参数就放在栈上那么 8(%rsp)就可以访问这个参数了,pushq操作分为两部分,首先是rsp-8,然后把数据放进去。这个时候其实数据就是在rsp到rsp+8的区域中,也就是说我们直接movq (%rsp)…就可以访问到我们刚才放进去的元素了
我们这里不采用(%rsp)的原因是因为调用函数是callq,在最后应该是把rip放到栈中,所以要加8到下一个元素。
如果有8个参数,那么栈中要先存第8个,然后再存第7个。
栈上的局部储存由于现代编译器的不断优化和寄存器的增多,我们一般不把局部变量放到栈中,但是有的时候我们不得不把他们放到栈中。
寄存器不足时
某一变量用了&(取地址),因此这个时候不得不把它放到栈中,这样才 ...
Hadoop和Spark
Mapreducemapreduce是一种编程模型,它的基本思想就是分而治之。
map()负责将一个任务分解成若干个小的任务,这些小的任务可以交给不同的计算节点去处理,Reduce()负责把这些计算结果进行合并。
例如统计词频:a a aaa aaabcdebcabcabc想要统计这些次出现的次数首先进行map,例如有4个工作节点,我们可以把每两行给一个工作节点,然后这些工作节点就会进行词频统计,最后得到('a', 3), ('aa', 1)('aaa', 1), ('bc', 1)('de', 1), ('bc', 1)('abc', 2)之后把这些计算结果发给主节点,然后主节点进行reduce, 也就是把各个工作节点产生的结果组合到一起。('a', 3), ('aa', 1), ('aaa', 1), ('bc', 2), ('de', 1), ...
js基础语法
html中使用javascript将js插入html主要是用<script>元素,这个元素有八个属性
async: 表示应该立即下载脚本,但是不阻止其它页面动作(如加载页面,加载其他脚本等),只对外部脚本有效.async标记的脚本之间没有前后顺序关系。
charset: 指定字符集,但是很少用
crossorigin: 配置相关请求的CORS(跨源资源共享)设置。crossorigin="anonymous"表示配置文件请求不必设置凭据标识,而use-credentials表示出站请求会包含凭据
defer: 表示文档解析和显示完成后再执行脚本也是可以的,即推迟执行。有时候会在DOMContentLoaded事件之前执行(此时已经解析完</html>了),并且推迟执行的脚本也会按顺序执行。但是都不是绝对的。
integrity: 允许比对接收到的资源和指定的加密签名来验证子资源完整性
src: 表示包含的外部文件地址
type: 这个值始终是”text/javascript”
使用script时,一般把<script>元素放在 ...
js对象
基础操作创建自定义对象的方式通常是创建一个Object的新实例,然后添加属性和方法let person = new Object();person.name = 'Nicholas';person.age = 29;person.job = 'engineer';person.sayName = function(){ console.log(this.name);}
这是早期创建对象的方式。如果使用对象字面量可以这样写let person = { name: 'Nicholas'; age: 29; job: 'engineer'; sayName() { console.log(this.name); }}
属性类型属性有些是对象中的成员变量,还有些类似于c#中的属性(property).这些属性都有一些内置的attribute(特性),我们可以修改这些特性。为了标识某一个内部特性,一般使 ...
js变量、作用域、内存
原始值和引用值原始值就是最简单的数据类型,而引用值就是对象。
原始值不能有属性,尽管给原始值添加属性不会报错
let name = 'Nicholas';name.age = 27;console.log(name.age);//undefined
原始值赋给另一个值时,会在内存中复制一份,因此两份互不干扰let num1 = 5;let num2 = num1;num1 = 3;console.log(num2);//5
把引用值从一个变量赋给另一个变量时,实际上是复制了指向这个对象的指针(对象本身在堆中),因此一个变量对对象进行修改另一个也会修改。
js函数只存在值传递,也就是说参数是原变量复制一份传进去的,也就是说如果传递的是对象我们在函数中修改了对象的属性在函数外也是成立的。
function setName(obj){ obj.name = 'b';}let person = new Object();setName(person);console.log(person.name);//b---function ...
vi
启动vi语法: vi [-rR] [file…]
如果用vi启动了一个不存在的文件,那么他将新建这个文件。如果只输入vi,那么他将在当前文件夹下创建一个空文件
-R 以只读模式启动
vim是vi的进阶版本
语法: vi -C [-rR] [file…]
-C是兼容模式,它将尽可能的和vi保持一样
命令模式和编辑模式当使用vi时,存放数据的区域叫做编辑缓冲区(editing buffer),如果想编辑文件,那么vi就将文件的内容复制到缓冲区中,然后再缓冲区中编辑。因此处理的其实是文件的副本。
在命令模式中,输入的一切字符都是命令。例如,在命令模式中,单个字母x就是删除一个字符。dd就是删除整行的命令。
第二种模式就是输入模式。这种模式下,任何内容都会输入到缓冲区中。
在启动过程中,vi完成三件事情。将文件中的内容复制到编辑缓冲区,将光标定位到缓冲区第一行的开头。最后vi进入命令模式。
数据的恢复和关闭vi当使用vi时,数据会保存在编辑缓冲区内,因此当程序非正常终止时,临时文件依然存在。使用-r(recovery)就可以重新打开缓冲区
如果需要先保存再退出,使用的命令时ZZ,即先按下shi ...
MPI编程
简介MPI是一种编程规范,它的实现是一个库,主要在Fortran和c语言上实现。它的目的是实现进程间通讯。
MPI基本函数开始与结束
MPI_Init(&argc, &argv): 利用输入进来的参数进行初始化
MPI_Finalize(): MPI终止时调用
#include <stdio.h>#include <mpi.h>void main (int argc, char * argv[]){ int err;err = MPI_Init(&argc, &argv); printf("Hello world!\n"); err = MPI_Finalize();}运行mpirun -n 4 ./hello.c
身份标识
MPI_COMM_WORLD: 获得通信域(也就是同一组内的进程,例如上面mpirun -4代表启动4个进程,他们属于同一组)
MPI_Comm_size(MPI_COMM_WORLD, &size): 获得进程个数,赋值给size
MPI_ ...
字符串之trie树(字典树)
作用字典树作用正如其名,就是用来找字符串的。先给你一堆字符串作为字典,然后给其他字符串看这些字符串是否在开始给的字符串里,可能对于这种情况首先想到的便是map,但map查找效率十分低下,碰到大量的数据时很容易TLE,于是字典树便到了我们眼前
基本它实际上就是一个树,类似于求前缀码,用第一层表示第一个字母,第二层表示第二个字母,以此类推,而每个字母不一定都有,这样便减小了搜索所需时间
在计算机中,trie树可以用一个二维数组a[i][j]来表示。第一个元素i另外还有一个问题是这么多单词,怎么知道这个单词是不是终点呢?,可以通过insert函数中的ch[u][c]=sz++;来判断,如果等于一,则说明这是终点,
代码实现:
模板#include<bits/stdc++.h>#define CLR(x) memset(x,0,sizeof x)using namespace std;const int maxnnode=1000;const int maxn=1000;//字母表为全体小写字母的Trie struct Trie{ int ch[maxnnode][ ...
html元素
仅是记笔记,没有考虑格式。
title: 几乎所有元素都可以插入,代表提示信息。id: 元素的标识,可以用于定位<html></html> 表示这是一个html<title></tilte> 网页标题<head></head>: 头部<body></body> 主体<p></p> 段落<h1></h1>,<h2></h2> 文章内部标题<a href = "link..." title=" ..." id="..." target=" "> ...</a> a元素可以让一个页面跳转到另一个页面(链接),href指定了目标文件,link代表的就是链接,如果使用相对目录,那么根目录就是当前html所在目录,可以用.. 返回上级目录。可以直接在link后加一个#... ,之后会直接跳转到#锁指向的位置(作用和id一样).t ...
加法溢出和乘法溢出
无符号数加法便是两个数相加,如果超过最大值那么就截断超过的位数。截断超过的位数也就相当于mod2^n,因为这样做超过的位都会被模去变成0.
有符号数加法相对复杂。先是变成无符号数加法,加完 之后再按有符号数编码去理解,这样就会产生两种溢出,正溢出和负溢出。
首先,如果两个数一正一负,则不可能产生进位。
正溢出很好理解,最高一位为符号位,假如两个正数一加,超过了最大值,那么第2^w-1位就会变成1,这一位是符号位,因此这个数字便会变成负数。
如果是负溢出,因为两个数最高位一定为1,如果2^w-2位没有发生进位的话,那么就产生了溢出,此时最高位为0,变成正数
例如:10111111+10111111(-65)=01111110(126)
所以说如果是正数溢出,则需要-2^m,如果是负溢出,则需要加上2^m
判定是否发生溢出s=a+b;if((a>0==b>0)&&(a<0!=s<0)){ cout<<"发生溢出"<<endl;}else{ cout<<& ...