2013年11月9日星期六

用loadMovie调用外部*.swf文件

  (一)调用外部*.swf文件加载到影片剪辑中
   外部*.swf文件要和编辑的Flash文件放在同一目录下
   1.新建立一个空的影片剪辑mymc,把它放在场景中,实例名是:mymc.
   2.新建一层,制作两个按扭(一个调用,一个清除)拖放到此层中
   3.调用按扭上的AS代码:
on(release){//鼠标离开按扭后执行下面的代码;
 loadMovie("flash8.swf","mymc");//加载外部的"flash8.swf"文件到"mymc"空影片剪辑中;
 mymc._x=70;//加载影片的X轴坐标;
 mymc._y=20;//加载影片的Y轴坐标;
 mymc._xscale=70;//加载影片的宽度;
 mymc._yscale=70;//加载影片的高度
}
  清除按扭上的AS代码:
on(release){//鼠标离开按扭后执行下面的代码
 unloadMovie(mymc);//删除用loadMovie加载的*.swf文件;
}
  Ctrl+Enter测试
(二)调用外部*.swf文件并加载到时间轴上
   外部*.swf文件要和编辑的Flash文件放在同一目录下
   1.制作两个按扭(一个调用,一个清除)拖放到场景中
   2.调用按扭上的AS代码:
on(release){//鼠标离开按扭后执行下面的代码
 loadMovie("flash8.swf",1);//加载外部的"flash8.swf"文件到场景中,层深为1;
}
  清除按扭上的AS代码:
on(release){//鼠标离开按扭后执行下面的代码
 unloadMovie(1);//删除层深为1的用loadMovie所加载的"flash8.swf"文件
}
  Ctrl+Enter测试 。
  当然二和三的代码都可以写在帧上。

2010年10月13日星期三

ntfs-3g and fuse

I have 3 ntfs partitions in my disk, so in order to read and write these partitions, I install ntfs-3g and fuse.
As before, I refer to http://gentoo-wiki.com/HOWTO_NTFS_write_with_ntfs-3g as a guide. But to my surprise, it sucks.

my system is:
# uname -a
Linux styx 2.6.24-gentoo-r3 #34 SMP Sun Mar 16 04:45:29 CST 2008 i686 Intel(R) Pentium(R) 4 CPU 3.00GHz GenuineIntel GNU/Linux


I strictly did as that guide does:

"The driver ebuild depends on sys-fs/fuse (portage will install it automatically as a dependency of the ntfs3g ebuild). Furthermore, it depends on the version of the fuse kernel module. Basically, just ensure that the "File Systems -> Filesystem in Userspace support" option is set as MODULE in your kernel configuration. If it's enabled or disabled, set to build as a module, compile, install and modprobe it."

So I set "File Systems -> Filesystem in Userspace support" into a module of kernel, and "# emerge -av ntfs-3g", but I get errors as follows:
# ntfs-3g /dev/sda6 /mnt/xpe
module fuse not found
# modprobe fuse
module fuse not found
# lsmod | grep fuse
nothing
# zgrep -i fuse /proc/config.gz
nothing

It is so weird, you known it works before. So I then compiled "File Systems -> Filesystem in Userspace support" into kernel directly, instead of just a module. But when I tried to mount ntfs partitions, I got the same result.

At last, I kicked "File Systems -> Filesystem in Userspace support" out of the kernel, and just emerge sys-fs/fuse instead. Fortunately it works. Happy and merry~~

Any one who has this problem could do as I do. Good luck!


PS:
a page well tell you how to modify your own "fstab" file
http://www.tuxfiles.org/linuxhelp/fstab.html

ASCII码的读法

看到一篇文章关于ASCII码的读法,遂摘录下来:

Table C-1. ASCII Pronunciation Guide

CharacterPronunciation
!bang, exlamation
*star, asterisk
$dollar
@at
%percent
&ampersand
"double-quote
'single-quote, tick
( )open/close bracket, parentheses
<less than
>greater than
-dash, hyphen
.dot
,comma
/slash, forward-slash
\backslash, slosh
:colon
;semi-colon
=equals
?question-mark
^caret (pron. carrot)
_underscore
[ ]open/close square bracket
{ }open/close curly brackets, open/close brace
|pipe, or vertical bar
~tilde (pron. ``til-duh'', wiggle, squiggle)
`backtick

关于C语言中的interposition(插入)

关于C语言中的interposition的,在编译,链接,运行时三个阶段分别实现,其实不是太理解,现写下网址:
http://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15213-s03/src/interposition/mymalloc.c

"Hacking Vim"读书笔记(1)

以前喜欢Emacs,现在发现Vim确实也很好用,主要是它的几个模式好,按键要比Emacs少得多,阅读代码只要一只手扔在键盘上即可,另一只手还可以打打响指什么的。要想用好Vim,首先要搞定配置,此书讲得就是这个。因为是一些实践性的活动,因此还是记下,否则就会忘掉。以下就按照书的章节来总结。

1. personalizing Vim
1) 字体
gui下字体设置:set guifont=Courier\ New\ 12, Arial\ 10 ( vim在term下字体与term本身相同,因此不用设置)
根据不同的filetype选择不同字体:autocmd BufEnter *.txt set guifont=Arial\ 12
2) 颜色
colorscheme设置:colorscheme zenburn (desert)。desert和zenburn都是不错的scheme。
个人高亮设置:highlight MyGroup ctermbg=red guibg=red ctermfg=yellow guifg=yellow term=bold
(创建新高亮组,每组相同高亮设置)
match Group /pattern/ (将特定pattern绑定在高亮组Group上,如match ErrorMsg /^Error/)
so $VIMRUNTIME/syntax/hitest.vim,可以查看所有的预设的color group
3) 状态栏设置
set statusline=%F%m%r%h%w\ [FORMAT=%{&ff}]\ [TYPE=%Y]\ [ASCII=\%03.3b]\ [HEX=\%02.2B]\ [POS=%04l,%04v][%p%%]\ [LEN=%L]
set laststatus=2 (状态栏总是显示)
set laststatus=0 (状态栏总是不显示,默认)
help 'statusline' (状态栏设置帮助)
4) 状态栏和菜单栏
用ctrl-F2触发menubar和toolbar的隐藏和显示:
map :if &guioptions =~# 'T'
\set guioptions-=T
\set guioptions-=T
\else
\set guioptions+=T
\set guioptions+=m
\endif
set guioptions-=m (关闭menubar)
set guioptions-=T (关闭toolbar)
help 'guioptions' (获取帮助)
为menubar增加条目: menu Tabs.&Next :tabnext
(增加一个Tabs菜单项,下面有个Next条目并设快捷键,执行 :tabnext 命令)
其它还有imenu, nmenu, vmenu, cmenu等等,具体看帮助
为toolbar增添icon:amenu icon=/path/to/icon/myicon.png ToolBar.Bufferlist :buffers
toolbar在vim中只不过是menubar的一个项目,只不过有着一个特殊的名字ToolBar
5) 设置tabs (vim7.0以上)
set guitabtooltip=%!bufname($) 鼠标经过时,跳出tooltip提示文件的路径+文件名
set tabline=%!ShortTabLine() 设置tab上的文字,此处表示用ShortTabLine这个函数来设置tab上的显示
set guitablabel=%{ShortTabLabel()} gvim上tab的显示设置,ShortTabLabel()也是一个自己写的函数
6) 工作环境设置
set cursorline (set nocursorline) 设置cursorline
set cursorcolumn (set nocursorcolumn) 设置cursorcolumn,这个比较怪异
highlight CursorLine guibg=lightblue ctermbg=lightgray 设置cursorline(cursorcolumn)颜色
set number 行号
set numberwidth=XXX 行号显示部分的宽度
highlight LineNr **** 行号显示区域的高亮设置,LineNr是它的color group
set spell 拼写检查
set spelllang=en,da,de,it 拼写检查的语言
set spellsuggest=5 将拼写错误的改正提示设为5个(normal mode下在错误单词下按 z= 得到)
set ballooneval vim中balloon就是编辑缓冲区的tooltip
set balloondelay=400
set ballonexpr="textstring" 将tooltip设置成静态文字串textstring;
这里也可以将其设为动态的,用函数实现便可。具体可参考hacking vim P40
abbreviate Abbreviations for all modes
iabbrev Abbreviations for insert mode, 如iabbrev myAddr Pudong New District, Shanghai。
当键入myAddr后跟空格或回车时,自动转变成后面真实地址
cabbrev Abbreviations for the command line only
[这里一个好的习惯是将所有的缩写词都放到一文件(如bxAbbrev.vim),然后在.vim文件中source进来就可以了]
8) 按键绑定
map for the modes Normal, Insert, Visual and Command-line
imap for Insert mode only
cmap for Command-line mode only
nmap for Normal mode only
vmap for Visual mode only
help key-mapping
各键的表示:C for Ctrl, A for Alt, M for Meta, for Escape, for enter
举例:不同模式下保存文件
map :w
imap :wa

Harris Corner Detector

Implementation of "Harris Corner Detector"(By Me) - 2007/10/23 20:02
Recently, I implemented Harris corner detector in Matlab. It's so simple, so I will extend it into multiscales, according to schmid's paper. The code is listed as follows:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Step one, detect the points of interest using Harris corner dector.
% Later, I will use Multisalce-Harris corner detector to get such property of
% invariace of scale.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function poiMark = StyxDetectFeature(filename)

imgOrigin = rgb2gray(imread(filename));
[height, width] = size(imgOrigin);

imgTmp = double(imgOrigin)/255;

% point-of-interest marking matrix, 1 for interesting points
poiMark = zeros([height, width]);

K = 0.06;

% Integration Gaussion Window
itgGaus = [2, 4, 5, 4, 2;...
4, 9, 12, 9, 4;...
5, 12, 15, 12, 5;...
4, 9, 12, 9, 4;...
2, 4, 5, 4, 2] ./ 115;
IxIx = zeros([height, width]);
IxIy = zeros([height, width]);
IyIy = zeros([height, width]);

for i = 1 : height - 1
for j = 1 : width - 1
IxIx(i, j) = (imgTmp(i, j) - imgTmp(i+1, j))^2;
IxIy(i, j) = (imgTmp(i, j) - imgTmp(i+1, j))*(imgTmp(i, j)-imgTmp(i, j+1));
IyIy(i, j) = (imgTmp(i, j) - imgTmp(i, j+1))^2;
end
end
IxIx(:,end) = IxIx(:, width-1);
IxIx(end, :) = IxIx(height-1, :);
IxIy(:,end) = IxIy(:, width-1);
IxIy(end, :) = IxIy(height-1, :);
IyIy(:,end) = IyIy(:, width-1);
IyIy(end, :) = IyIy(height-1, :);

M = size([2, 2]);
R = zeros([height, width]);

% plot the result
% figure(2);
% subplot(2, 1, 1);
% imshow(imgOrigin);
% subplot(2, 1, 2);
% imshow(imgOrigin);
% hold on

% select the candidate points
for i = 3 : height-2
for j = 3 : width-2
M(1,1) = sum(sum(IxIx(i-2:i+2, j-2:j+2).*itgGaus));
M(1,2) = sum(sum(IxIy(i-2:i+2, j-2:j+2).*itgGaus));
M(2,1) = M(1,2);
M(2,2) = sum(sum(IyIy(i-2:i+2, j-2:j+2).*itgGaus));
% get the eigenvalue of the Hessian Matrix
R(i, j) = det(M) - K * (M(1,1) + M(2,2))^2;
if R(i, j) > 0.0005
poiMark(i, j) = 0.1;
%plot(i, j, 'or');
end
end
end

% select the 8-way local maximum
for i = 2 : height-1
for j = 2 : width - 1
if poiMark(i, j) == 0.1 && R(i, j) == max(max(R(i-1:i+1, j-1:j+1)));
poiMark(i, j) = 1;
end
end
end

% hold off

% figure(1);
% subplot(1,2,1);
% imshow(imgOrigin);
% subplot(1,2,2);
% imshow(poiMark);

Retinex Source Code

以下为Styx的Retinex图像增强的源代码,写得粗糙了些,并且没有注释,不过看过Retinex论文的人应该可以看懂。包括两个,一个是single scale的,一个是multi-scales的。如果单独使用single scale的,就把其中的注释去掉,可以显示结果了。如要使用multi-scales的,因为它要调用single scale的函数,所以就single scale函数中的一部分就要加上注释,以供其用。

Retinex Single Scale Enhancing : Source Code(by Matlab)
function R = Retinex(I, scale, weight)
%RETINEX enhance the image
%I = double(imread(filename)) + 1;

[width, height, depth] = size(I);

FInverse = 0;
for i = 1 : width
for j = 1 : height
%Fk(i, j) = exp(-((i-width/2).^2 + (j-height/2).^2)/scale.^2);
Fk(i,j) = exp((-i.^2 - j.^2)/scale.^2);
FInverse = FInverse + Fk(i,j);
end
end

coff = 1 / FInverse;
Fk = Fk * coff;

Ift = zeros(size(I));

Ift = fft2(I);
Fkft = fft2(Fk);

tmpSumft = zeros([width, height, depth]);
for k = 1 : depth
tmpSumft(:, :, k) = Ift(:, :, k) .* Fkft;
end
tmpSum = ifft2(abs(tmpSumft));

R = weight * (log(I) - log(abs(tmpSum)));

%figure(2);
%subplot(2, 1, 1);
%imshow(uint8(I));
%subplot(2, 1, 2);
%maxp = max(R(:));
%minp = min(R(:));
%step = 255/(maxp - minp);
%imshow(uint8((R-minp)*step-1));

Retinex Multi-Scales Enhancing : Source Code(by Matlab)
function Output = RetinexMS(filename, scale, weight)
%RETINEXMS multiscale retinex method to enhance the image
OriginI = double(imread(filename)) + 1;
Output = zeros(size(OriginI));
for i = 1 : size(scale, 2)
Output = Output + Retinex(OriginI, scale(i), weight(i));
end

figure(2)
subplot(2,1,1);
imshow(uint8(OriginI - 1));
subplot(2,1,2);
maxp = max(Output(:));
minp = min(Output(:));
step = 255/(maxp - minp);
Output = uint8((Output-minp)*step - 1);
imshow(Output);


这里还有个问题,就是显示的问题,我直接将图像处理后的结果(都是些浮点数,大概在正负个位数之间)线性扩展到了0~255之
间,显示出来的图像虽然亮度增大,阴影部分清楚,但是过于偏白偏亮,实现过的人觉得这有什么错误吗?欢迎任何建议!

论虚函数和多态

虚函数和多态
2007-06-23 20:33

二.虚函数和多态

1. 先看以下的例子:

class Animal

{

private:

double weight;

string color;

public:

Animal(double a, string b) : weight(a), color(b) {}

virtual void roar() {cout << "roar in the base class!" <<>

void printName()

{cout << "my weight is: " <<>

};

class Cattle : public Animal

{

public:

Cattle(double a, string b) : Animal(a, b){}

//void roar(){ cout << "Cattle is roaring!" <<>

};

class Dog : public Animal

{

public:

Dog(double a, string b) : Animal(a,b) {}

//void roar(){ cout << "Dog is barking!" <<>

};

……

Animal *myAnimal;

Cattle myCattle(100, "brown");

Dog myDog(10, "black");

myAnimal = &myCattle; // 通过基类指针正确调用子类函数,这便是多态

myAnimal->roar();

//myAnimal->printName();

myAnimal = &myDog;

myAnimal->roar();

//myAnimal->printName();

//myCattle.printName();

//myDog.printName();

Polymorphism: the ability to assume many forms

注意点:

1. 派生类继承基类的所有,包括成员函数和成员变量,即使虚函数也将被继承。此处,对于public继承,派生类可以直接调用基类中的public成员函数,此public成员函数可以直接访问基类中的private变量;但是,派生类中不能不通过基类继承来的成员函数直接去访问基类的成员变量,如此处的weightcolor。它们一定要通过Animal类中的priteName()函数来访问。

2. 包含纯虚函数(virtual func( )=0)的类是抽象类,不能被实例化,否则编译出错。

3. 包含虚函数类的对象的内存空间:

举例:

Class Class1

{

public:

data1;

data2;

memfunc();

virtual vfunc1();

virtual vfunc2();

virtual vfunc3();

}

在内存空间中,表示如下:

从上表中可以看出每个含有虚函数的类,编译器为其分配一个虚函数表(vtable),表中每一项元素都指向一个虚函数的地址。同时,编译器为类加上一个成员变量,就是指向虚函数表的指针(vptr)

值得注意的是,C++类的普通成员函数并没有出现在Class1的内存区块中。可以将其想象成是C语言中的函数,它只是被编译器改过名字,并增加了一个this指针而已。因此不需要出现在类实例的内存中。

class1继承而来的类因为会继承它的虚函数,因此也有自己的vptr。派生类继承基类的虚函数表以及其它可以继承的东西。但是,如果我们在派生类中改写了虚函数时,虚函数表将改变:表中元素所指向的将不再是基类的函数地址,而是被修改的虚函数新地址。【此处一个重要信息就是:在未修改基类虚函数时,在派生类中调用,将会调用基类中的函数】

看下例:

class class2 : public class1

{

public:

data3;

memfunc();

virtual vfunc2(); // 修改了基类中的一个虚函数

}

4. upcasting

Class Father

{
public:
void func()
{
cout<<"this is the Father func!"< vfunc();
}
virtual void vfunc()
{
cout<<"this is the Father vfunc!"< }
private:
int m_data1;
}


Class Son

{
void vfunc()
{
cout<<"this is the Son vfunc!"< }
private:
int m_data2;
};

Farher father; Son son;
(1) ((father *)(&son))->vfunc();
(2) ((father)son).vfunc();
:在(2)中对象发生的截断,son已经完完全全的变成了father型对象,所以(2)调用的虚函数是father中的,但是我总觉得就算发生了截断,son对象中的vptr应该是不变的吧??,,为什么会变?为什么将子类指针强制转化为父类指针(1),不发生截断呢?

:所谓的切片(slice)就是指创建出了一个临时的对象,你没看到(father)son这是一个强制转换嘛。son中的vptr当然没变,(father)son已经是一个father类型的临时对象了,其vptr当然指向father类的了

论继承

继承
2007-06-23 20:33


以下就算是自己看jjh先生的《深入浅出MFC》的笔记吧
一.继承
1.派生类继承基类时,会将基类的成员函数和成员变量也继承下来。
2.关于this指针
对于以下一段代码,
class CShape
{
private:
int m_color;
public:
void setColor( int color) { m_color = color;}
};
class CRect : public CShape
{
public:
void display(){…}
};
假如拥有两个CRect实例的rect1和rect2的话,当你调用:
rect1.setColor(2);
rect2.setColor(3);
时,编译器实际上为你做出来的代码是:
CRect::setColor(2, (CRect*) &rect1);
CRect::setColor(3, (CRect*) &rect2);
这多出来的参数,就是所谓的this指针。而在类的定义之中,成员函数的定义被编译器变成了:
class CShape
{
private:
int m_color;
public:
void setColor( int color,(CShape*)this ) { this->m_color = color;}
};
this指针确保了同一类(此处是CRect类)的不同实例对象(此处两个对象是rect1和rect2)在调用同一个函数(此处是setColor())时能找到正确的作用对象,即不会混淆rect1的m_color和rect2的m_color。

窥探C语言函数调用约定(1)

2007-06-23 20:06
------by styx <参考了馨荣家园中的一篇文章,自己修正和补充之,对作者致以谢意>

在C语言中,假设我们有这样的一个函数:
int function(int a,int b);
假设其其调用者为main函数,代码如下:
Int main()
{
function(1, 2);
return 0;
}
当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈的数据结构来支持参数传递。
栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入数据,这个操作被称为压栈(Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修改。用户也可以从堆栈中取走栈顶,称为弹出栈(pop),弹出栈后,栈顶下的一个元素变成栈顶,栈顶指针随之修改。
函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。在参数传递中,有两个很重要的问题必须得到明确说明:
l 当参数个数多于一个时,按照什么顺序把参数压入堆栈
l 函数调用后,由谁来把堆栈恢复原装
在高级语言中,通过函数调用约定来说明这两个问题。常见的调用约定有:
1 stdcall
2 cdecl
3 fastcall
4 thiscall
5 naked call
stdcall调用约定
stdcall很多时候被称为pascal调用约定,因为pascal是早期很常见的一种教学用计算机程序设计语言,其语法严谨,使用的函数调用约定就是stdcall。在Microsoft C++系列的C/C++编译器中,常常用PASCAL宏来声明这个调用约定,类似的宏还有WINAPI和CALLBACK。
stdcall调用约定声明的语法为(以前文的那个函数为例):
int __stdcall function(int a,int b)
stdcall的调用约定意味着:
l 参数从右向左压入堆栈
l 函数自身修改堆栈
l 函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸
以上述这个函数为例,参数b首先被压栈,然后是参数a,函数调用function(1,2)调用处翻译成汇编语言将变成:
push 2 第二个参数入栈
push 1 第一个参数入栈
call function 调用参数,注意此时自动把cs:eip入栈
而对于函数function自身,则可以翻译为:
push ebp 保存ebp寄存器,该寄存器将用来保存调用函数堆栈的
栈顶指针,可以在函数退出时恢复
mov ebp, esp 保存堆栈指针
mov eax, [ebp + 8H]
堆栈中ebp指向位置之前依次保存有ebp, cs:eip, a, b;ebp+8指向a;
add eax, [ebp + 0CH] 堆栈中ebp + 12处保存了b,此处就是运算a+b;
mov esp, ebp 恢复esp
pop ebp
ret 8 函数体负责清栈;两个int型参数共用了8byte空间的堆栈
而在编译时,这个函数的名字被翻译成_function@8
注意不同编译器会插入自己的汇编代码以提供编译的通用性,但是大体代码如此。其中在函数开始处保留esp到ebp中,在函数结束恢复是编译器常用的方法。
从函数调用看,2和1依次被push进堆栈,而在函数中又通过相对于ebp(即刚进函数时的堆栈指针)的偏移量存取参数。函数结束后,ret 8表示清理8个字节的堆栈,函数自己恢复了堆栈。< Why 8 bytes? >

窥探C语言函数调用约定(2)

cdecl调用约定
cdecl调用约定又称为C调用约定,是C语言缺省的调用约定,它的定义语法是:
int function (int a ,int b) //不加修饰就是C调用约定
int __cdecl function(int a,int b)//明确指出C调用约定
相同点:cdecl调用约定的参数压栈顺序是和stdcall是一样的,参数首先由右向左压入堆栈。
不同的是:函数本身不清理堆栈,调用者负责清理堆栈。由于这种变化,C调用约定允许函数的参数的个数是不固定的,这也是C语言的一大特色。对于前面的function函数,使用cdecl后的汇编码变成:
调用者main函数处:
push 2
push 1
call function ;call完成后,ebp已是原来的堆栈基址,而esp也指向了返回地址之上的那个地址,此处就是数字1所在的地址。
add esp, 8
;这里调用者main函数在清理堆栈。执行”add esp, 8”这一句后,使
栈顶指针esp指回到了2和1被压栈之前的状态的栈顶。堆栈被main函
数,即调用者本身清理干净
被调用函数_function处
push ebp 保存ebp寄存器,该寄存器将用来保存堆栈的栈顶指针,可
以在函数退出时恢复
mov ebp, esp 转入新的堆栈(new stack frame)
mov eax, [ebp + 8H] 堆栈中ebp指向位置之前依次保存有ebp, cs:eip,
a, b;ebp +8指向a
add eax, [ebp + 0CH] 堆栈中ebp + 12处保存了b
mov esp, ebp 恢复esp
pop ebp 此处一pop,esp的值升4,指向了return address
ret 循着return address回了老家去了。ret要做两个动作:1) pop the return address from the stack. 2)jump to the location that address directs. 因此,ret完后,esp指针的值再增加4个bytes
简要过程图如下所示:


该修饰自动在函数名前加前导的下划线,因此函数名在符号表中被记录为_function
由于参数按照从右向左顺序压栈,因此最开始的参数在最接近栈顶的位置,因此当采用不定个数参数时,第一个参数在栈中的位置肯定能知道,只要不定的参数个数能够根据第一个后者后续的明确的参数确定下来,就可以使用不定参数,例如对于CRT中的sprintf函数,定义为:
int sprintf(char* buffer,const char* format,...)
由于所有的不定参数都可以通过format确定,因此使用不定个数的参数是没有问题的。
fastcall调用约定
fastcall调用约定和stdcall类似,它意味着:
l 函数的第一个和第二个DWORD参数(或者尺寸更小的)通过ecx和edx传递
l 其他参数通过从右向左的顺序压栈
l 被调用函数清理堆栈
函数名修改规则同stdcall
其声明语法为:int fastcall function(int a,int b)

窥探C语言函数调用约定(3)

thiscall调用约定
thiscall是唯一一个不能明确指明的函数修饰,因为thiscall不是关键字。它是C++类成员函数缺省的调用约定。由于成员函数调用还有一个this指针,因此必须特殊处理,thiscall意味着:
l 参数从右向左入栈
l 如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈。
l 对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈
为了说明这个调用约定,定义如下类和使用代码:
class A
{
public:
int function1(int a,int b);
int function2(int a,...);
};
int A::function1 (int a,int b){ return a+b;}
int A::function2(int a,...)
{
va_list ap;
va_start(ap,a);
int i;
int result = 0;
for(i = 0 ; i < a ; i ++)
{
result += va_arg(ap,int);
}
return result;
}
void caller()
{
A a;
a.function1 (1,2);
a.function2(3,1,2,3);
}
caller函数被翻译成汇编后就变成:
//函数function1调用
0401C1D push 2
200401C1F push 1
100401C21 lea ecx, [ebp-8]
00401C24 call function1 注意,这里this没有被入栈
//函数function2调用
00401C29 push 3
300401C2B push 2
200401C2D push 1
100401C2F push 3
300401C31 lea eax, [ebp-8] 这里引入this指针
00401C34 push eax
00401C35 call function2
00401C3A add esp, 14h
可见,对于参数个数固定情况下,它类似于stdcall,不定时则类似cdecl
naked call
这是一个很少见的调用约定,一般程序设计者建议不要使用。编译器不会给这种函数增加初始化和清理代码,更特殊的是,你不能用return返回返回值,只能用插入汇编返回结果。这一般用于实模式驱动程序设计,假设定义一个求和的加法程序,可以定义为:
__declspec(naked) int add(int a,int b)
{
__asm mov eax,a
__asm add eax,b
__asm ret
}
注意,这个函数没有显式的return返回值,返回通过修改eax寄存器实现,而且连退出函数的ret指令都必须显式插入。上面代码被翻译成汇编以后变成:
mov eax,[ebp+8]
add eax,[ebp+12]
ret 8
注意这个修饰是和__stdcall及cdecl结合使用的,前面是它和cdecl结合使用的代码,对于和stdcall结合的代码,则变成:
__declspec(naked) int __stdcall function(int a,int b)
{
__asm mov eax,a
__asm add eax,b
__asm ret 8 //注意后面的8
}
至于这种函数被调用,则和普通的cdecl及stdcall调用函数一致。
函数调用约定导致的常见问题
如果定义的约定和使用的约定不一致,则将导致堆栈被破坏,导致严重问题,下面是两种常见的问题:
l 函数原型声明和函数体定义不一致
l DLL导入函数时声明了不同的函数约定
以后者为例,假设我们在dll种声明了一种函数为:
__declspec(dllexport) int func(int a,int b); //注意,这里没有stdcall,使用的是cdecl
使用时代码为:
typedef int (*WINAPI DLLFUNC)func(int a,int b);
hLib = LoadLibrary(...);
DLLFUNC func = (DLLFUNC)GetProcAddress(...) //这里修改了调用约定
result = func(1,2);//导致错误
由于调用者没有理解WINAPI的含义错误的增加了这个修饰,上述代码必然导致堆栈被破坏,MFC在编译时插入的checkesp函数将告诉你,堆栈被破坏了。

2008年12月28日星期日

关于intel 915集成显卡驱动的笔记

按照intel官方主页上的提示,装了n久,试验了n久,成功了一小部分,记下。
显卡驱动的安装分成4个部分:
1. 内核相关部分的drm支持,可以用三种方法安装
a) 编译内核的时候加入drm支持,一般在 Devices Drivers --> Graphic Drivers --> DRM下面,编译进内核或编译成module(即m)均可
b) 安装x11-base/x11-drm包,注意此时内核中的drm支持应该去掉,否则包安装失败(本人的是gentoo,其他的如Debian,Suse或FC也有相对应的包,安装上即可)。
c) 到代码仓库中git出源代码来安装。
# git clone git://anongit.freedesktop.org/git/mesa/drm
这个drm文件夹下包含了两个东东,一个是libdrm,步骤2要用; 一个是drm内核驱动,此部分关注。
# cd drm/linux-core
# make
# cp *.ko /lib/modules/`uname -r`/kernel/drivers/char/drm/
接着修改/libmodules/`uname -r`/modules.dep文件,照着文件的格式来作,将modules都添入,使得kernel能找到它们。a)和b)中这一步已经自动做好了,所以不用手工改这个文件。
这三种方法得到的都是如i810.ko, i915.ko, i835.ko, intel.ko之类的文件,用modprobe可以加载之。

2. libdrm的安装, 可用两种方法做:
a) 源代码安装。1 c)中的代码中包含了libdrm,所以直接编译:
# cd drm
# ./autogen.sh --prefix=/usr --exec-prefix=lib # 加上两个prefix主要是用来覆盖系统原有的libdrm.so等库文件
# make && make install
b) 包安装(同样这里是gentoo的,其它系统类似)。
# emerge libdrm
此步生成的文件是libdrm.so, libdrm.la, libdrm...等一系列库文件。

3. xorg intel显卡2D驱动的安装,同样两种方法都可以:
a) 源代码安装
# git clone git://anongit.freedesktop.org/git/drivers/xf86-video-intel
# ./configure --prefix=${XORG_DIR}
# make && make install
注意,这里XORG_DIR是xorg-server被安装的主目录,我的系统上是/usr/X11R6
b) 包安装
# emerge -av xf86-video-intel
此步生成的文件是xorg的intel显卡2D驱动,形如intel_drv.so, i810_drv.so。
还有一个要注意的问题是,如果第2步中使用的是源代码安装,第3步中的./configure或者是emerge可能会有错误,抱怨libdrm没有安装,这主要是因为虽然安装了,但package系统没有找到它。所以为了能找到它,需要
# pkg-config /lib/pkgconfig
来更新libdrm库的信息。然后用
# pkg-config --modversion libdrm
来看看libdrm库文件的版本号,输出正确版本号,说明信息更新。然后在安装3就可以了。我的系统libdrm库安装在/lib下,所以pkg信息就在目录/lib/pkgconfig下。实际上是一个.pc文件,对于libdrm库,就是libdrm.pc。

4. xorg 下显卡3D驱动
同样用包安装(emerge mesa) 或者 源代码安装(git clone git://anongit.freedesktop.org/git/mesa/mesa; ./configure; make && make install)

至此全部安装完毕,将xorg.conf文件中的显卡module的driver改成intel,将"load dri" 以及"load glx"前面的"#"去掉。重新启动X,happy
另外可以用glxgears以及glxinfo来测试显卡性能以及dri打开与否。这两个东东是要安装的(emerge mesa-util?忘掉了,差不多吧8-/)

-----------------------------------------------------------------------------------------------
另外几个问题:
1) gentoo中自动加载模块可以放到/etc/modules.autoload.d/kernel-2.6文件中,如intel-agp
2) 好相不同的内核驱动放置的目录不同,新内核如我的2.6.27-r5内核,i810.ko放在/lib/modules/`uname -r`/kernel/drivers/gpu/drm/目录下,而老内核放在/lib/modules/`uname -r`/kernel/drivers/char/drm/目录下,这个问题其实困扰了我很久。
3) 手工加内核模块进/lib/modules/`uname -r`/目录下,一定记住加完后更新/lib/modules/`uname -r`/modules.dep文件,我现在只知道手工更新文件,是否能自动更新?待求解
4) 我的intel 915G显卡glxgears测试下来,竟然只有260多fps,虽然看见dri=yes了,估计还只是software的dri,硬件dri还没有搞定,实在是太难弄了。

And now, my X can't work properly once again (after a recompilation of my kernel). Terrible thing, terrible things all!

2008年11月10日星期一

回溯法(1)

话说回溯通过剪枝可以在性能上大大高于暴力,这是很好理解的(另外,今天才知道溯读作su第四声而不是shuo),但是比起DP来,时间复杂度还是很高的。典型的回溯解空间有子集树控件,时间复杂度为指数(如2^n);或者排列树,时间复杂度为阶乘(n!)。回溯到思想就是走迷宫,走到死胡同了,立即退回去,走另外一条路,以此往复。
程序上,可以用递归或者非递归实现,个人认为递归实现起来简单些,只要注意将状态量适时更新就可以了。下面贴出自己的几个实现程序。

1. 递归实现0-1背包的回溯
// W 背包重量数组, V 货物价值数组,i 当前考虑货物,n 总货物数,C 背包容量
// val 当前背包装入的价值,flag 当前遍历的货物状态数组(1代表要装入背包,0表示不要)
// maxflag 能取得最大价值的货物装法,maxval 背包能装入最大价值
void backtrack_cur(int* W, int* V, int i, int n, int C,
int val, int* flag, int* maxflag, int& maxval)
{
if (i == n) // 遍历完货物一趟
{
if (maxval < val){ // 当前价值与已知总价值比
maxval = val;
copy(flag, flag+n, maxflag); // 记下价值最大时的货物标志
}
return;
}

// 背包不装入货物i
backtrack_cur(W, V, i+1, n, C, val, flag, maxflag, maxval); //递归

// 背包装入货物i,如果不能装入,则剪枝
if (W[i] <= C) {
val += V[i];
C -= W[i];
flag[i] = 1;
backtrack_cur(W, V, i+1, n, C, val, flag, maxflag, maxval); //递归
}
flag[i] = 0; // 回溯,即走回去,只是flag状态清除到以前,这样才能回溯
}

调用此函数就比较简单:backtrack_cur(W, V, 0, n, capacity, 0, flag, maxflag, maxval);

2. 非递归实现0-1背包的回溯

1) 穷举实现(其实这里非回溯):
void enumerate(int* W, int* V, int n, int C)
{
int space = 1 << n; // 搜索空间,每一位代表一个货物,1代表装入背包
int flag = space; // 结果标记
int weight, value, maxvalue = 0;
for (int i = 0; i < space; ++i) {
value = 0; weight = 0;
for (int j = 0; j < n; ++j) { // 找出第j个货物是否装入背包
weight += ((i>>j) & 1)*W[j];
if (weight > C)
break;
value += ((i>>j) & 1)*V[j];
}
if (maxvalue < value) { // 当前最大价值
flag = i;
maxvalue = value;
}
}
print_rlt(flag, W, V, n, maxvalue); // 打印结果
}
2) 迭代实现回溯:
// 非递归实现,即用迭代/循环实现回溯
int backtrack_iter(int* maxflag, int* W, int* V, int n, int C)
{
int k = 0, val = 0, maxval = 0; // k用来指示当前所在位置(想迷宫那样的位置)
int rem = C; // 背包剩余容量
// 访问状态,对于背包,只有1,2,3三种状态:1试图放入背包,2不放入背包,
// 3状态访问完全,需要回溯到上一个节点。其它应用中,可以使用n+1个状态
int* visit = new int[n];
int* flag = new int[n];
memset(visit, 0, sizeof(int)*n); // 初始化
memset(flag, 0, sizeof(int)*n);

while (k >= 0) { // 当k<0时,回溯到初始
for (; k < n; ++k) { // 遍历当前路径一下的节点
visit[k]++; // 更新状态
if (visit[k] == 1) { // 第一次访问,试着装此货物到背包
if (W[k] <= rem) { // 能装下i货物,更新背包
val += V[k];
rem -= W[k];
flag[k] = 1;
} else { // 装不下,剪枝
break;
}
} else if (visit[k] == 2) { // 第二次访问,不装入背包
if (flag[k]==1) // 如果初次装入,则拿出
{
val -= V[k];
rem += W[k];
}
flag[k] = 0;
}
else { // visit[k]==3,第三次访问,状态遍历完,跳出以回溯
visit[k]=0; // 更新状态,下次重新访问
--k;
break;
}
}
if (maxval < val) { // 此次遍历得到当前最大值?
maxval = val;
copy(flag, flag+n, maxflag);
}

if (k==n) // 访问到叶节点?如果是,则变k,程序需要
--k;
}
return maxval;
}

2008年11月7日星期五

字符串匹配(2)-普通模式匹配

    主要是关于CLRS的笔记。所谓模式匹配,很好理解,就是在Emacs中crtl+s,或者Word中ctrl+f,即查找一个输入子串的位置。如找gitt在sagittarix中的位置。五中方法可以解决:1) 蛮力/朴素(两词一个意思);2) Rabin-Karp;3)有限自动机;4)Knuth-Morris-Pratt算法(KMP);5) Boyer-More(BM)算法
1) 朴素,老老实实一步一步做
2) 将子串转化成k进制数,比较数或者(模)
3) 有限自动机这东西不熟,希望自己能在下面给出它的代码
4) KMP的主要思想是用前缀函数来做,即用前缀函数记下某一模式前缀在模式中出现的下一位置,这样可以减少比较次数。
5) BM据说与KMP很像,但是不太清楚,有空时看看。
2)~5)之所以比1)快,是因为用到了预处理,以及充分削减了1)中的重复比较。下面给出有限自动机和KMP的代码。
有限自动机实现代码:
KMP实现代码:

字符串匹配(1) - 近似串匹配

    近似串就是并非完全相同的串(如happy和hsppy,或者pattern和patern),它们之间或者有字母不同,或者少掉一些字母,或者多一些字母。定义近似串之间的差别数为通过“修改,删去 或者 插入”使两串达到完全一致的操作次数。如aproxiomally与approximatly之间的差别数就是3。近似串匹配就是一种特殊的模式匹配--找到与模式差别数最少的子串。差别数为0,就退化为普通的串模式匹配问题。
    问题用动态规划解决。定义差别函数D(i,j)(0<=i<=m, 0<=j<=n)表示样本前缀子串p1,...,pi与文本前缀子串t1,...,tj之间的最小差别数,则D(m,n)表示样本P与文本T的最小差别数。当p1,...,pi与t1,...,tj对应时,D(i,j)有四种情况:
1) 字符pi与tj对应且pi=tj,此时对应差别数为D(i-1,j-1);
2) 字符pi与tj对应且pi!=tj,此时对应差别数为D(i-1,j-1)+1;
3) 字符pi多余,即pi对应于tj后的空,则对应差别数为D(i-1,j)+1;
4)字符tj多余,即pi对应于t(j-1),则对应差别数为D(i,j-1)+1;
所以,递推关系可如下表示(D矩阵大小为(m+1)x(n+1),第一行与第一列为padding,起重要作用):
当pi=tj时,D(i,j)=min(D(i-1,j-1), D(i-1,j)+1, D(i,j-1)+1);
当pi!=tj时,D(i,j)=min(D(i-1,j-1)+1, D(i-1,j)+1, D(i,j-1)+1);
初始值设置如下:
1) D(0,j) = 0;
2) D(i,0) = i;
    特别注意,D(0,j)的设置与递推关系一样,是本方法的两个最重要的关键点。D(0,j)设置为0表明计算差别数时t1,...,tj中前面一部分是不算进去的,即差别数只是统计了t_k,...,tj的部分(这里k为某一值)!举个例子,如求D[1][10]时(假设p1=t10), D[0][9]为0,从第9或第10个字母起开始统计代价,关键关键!!


程序代码如下:
int mymin(int& index, int a, int b, int c)
{
int min = a;
index = 1;
if (min > b){
min = b; index = 2;
}
if (min > c){
index = 3;
min = c;
}
return min;
}

// 递归打印匹配的近似子串
void print_pat_match(string& text, int** dirt, int i, int j)
{
if (i<0 || j<0) // boundary
return;

if (dirt[i][j] == 1) { // 1代表此处pi与tj是对应的
print_pat_match(text, dirt, i-1, j-1);
cout << text[j];
}
else if (dirt[i][j] == 2){ // 2代表往上,即pi对应空,或者说pi是多出的
print_pat_match(text, dirt, i-1, j);
}
else{ // 3代表往左,即tj对应空,或者说tj是多出的
print_pat_match(text, dirt, i, j-1);
}
}

int approx_match(string& text, string& pat)
{
int m = pat.size(); // 模式串长度
int n = text.size(); // 文本串长度
int i,j;
int** cost = new int*[m+1]; // 代价表
for (i = 0; i <= m; ++i)
cost[i] = new int[n+1];

int** dirt = new int*[m]; // 用于追踪匹配时的选择,值为1,2或者3,代表3方向
for (i = 0; i < m; ++i)
dirt[i] = new int[n];
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
dirt[i][j] = 0;

for (j = 0; j <= n; ++j) // 代价初始化
cost[0][j] = 0;
for (i = 0; i <= m; ++i)
cost[i][0] = i;

int index; int min_n = n, min_cost=100;
// 动态规划
for (i = 1; i <= m; ++i){
for (j = 1; j <= n; ++j){
if (text[j-1] == pat[i-1]){
cost[i][j] = mymin(index, cost[i-1][j-1],
cost[i-1][j]+1, cost[i][j-1]+1);
}
else{
cost[i][j] = mymin(index, cost[i-1][j-1]+1,
cost[i-1][j]+1, cost[i][j-1]+1);
}
dirt[i-1][j-1] = index; // 方向,方法可参看CLRS中解LCS的方法
}
}

for (j = 0; j <= n; j++){
if (min_cost > cost[m][j]){
min_n = j;
min_cost = cost[m][j];
}
}
print_pat_match(text, dirt, m-1, min_n-1); // 打印子串
cout << endl;
return min_cost; // 返回最小差别数
}

int main()
{
string text("Have a hsssppy day!"); // 文本串
string pat("happy"); // 模式
int diff = approx_match(text, pat); // 近似串差别数
cout << "diff = " << diff << endl;
}


空间复杂度仍然可以改进到O(n),不在赘述。

2008年11月6日星期四

最长连续公共子序列

最长公共子序列可以分成连续的子序列和非连续的子序列,后者在CLRS中称为LCS。这里主要给出的连续的公共子序列的算法及其程序。

主要有3种解法:1) 暴力法; 2) DP(矩阵型—>线型)3) 后缀树。


1) 简单,不用赘述。


2) Dynamic Programming (连续与不连续情况都适用)

\

e

d

c

e

d

n

n

0

0

0

0

0

1

c

0

0

1

0

0

0

e

1

0

0

1

0

0

d

0

1

0

0

1

0

t

0

0

0

0

0

0

我们把两个字符串看作矩阵的x-y轴:首先遍历字符串时,把两个字符相同的位置标记成1,不相同的地方标记成0。很容易证明,如果能形成公共子序列,则最长子序列一定是从某个位置开始的对角线的长度,所以我们需要做的就是统计对角线的长度。

例如str1=”ncedt”str2=”edcedn”生成的矩阵如左矩阵,遍历找到最长的对角线即可。


 

e

d

c

e

d

n

n

0

0

0

0

0

1

c

0

0

1

0

0

0

e

1

0

0

2

0

0

d

0

2

0

0

3

0

t

0

0

0

0

0

0

其实这里还可以优化,即在生成表格的时候,加上对角线上比它前面的元素,这样一遍遍历就可以生成左图所示矩阵,中间记下最大值便可,省下最后一趟比较,降低时间复杂度;空间复杂度也可降低,其实只要一维的数组便可以了,因为每次更新只用到上面一行(或左边一列,看你程序怎么写了,都可以)

这个直观上很好理解,但拆分到子问题的思想表达起来比较绕,就转一下别人的文字:

---------------------------------------------------------------------------------------------------------------------

定义f(m, n)为串XmYn之间最长的子字符串的长度并且该子字符串结束于Xm Yn。因为要求是连续的,所以定义f的时候多了一个要求,即字符串结束于Xm Yn

于是有f(m, 0) = f(0, m) = 0
如果xm != yn, f(m, n) = 0
如果xm = yn, f(m, n) = f(m-1, n-1) + 1
因为最长字符串不一定结束于Xm Yn末尾,所以这里必须求得所有可能的f(p, q) | 0 <>最大的f(p, q)就是解。依照公式用Bottom-up DP可解。

代码就不写了,简单。但是它与其它的DP填表又有所不同,不是直接得出最大的填到表格最后一格中,而是找出格子中的最大值,其中差别,细细体会。刚开始我在想的时候,就是受了之前的做法的影响,迟迟得不到思路,唉~~


3) 广义后缀树(generalized )

关于后缀树(suffix tree)的材料:

http://www.allisons.org/ll/AlgDS/Tree/Suffix/#demoForm

http://www.answers.com/suffix-tree

http://www.cise.ufl.edu/~sahni/dsaaj/enrich/c16/suffix.htm#substring

http://www.nist.gov/dads/HTML/suffixtree.html

http://blog.csdn.net/love_apple/archive/2007/02/12/1508758.aspx

广义后缀树只是简单修改,如str1str2一起建一棵后缀树,然后每个子串标记其出处,最长的来自两个串的子串相连就是最长公共连续子串。后缀树的构建比较复杂,现在还没有时间实现。