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函数将告诉你,堆栈被破坏了。