C语言函数调用的三种方式并分别举一例。

C语言函数调用的三种方式并分别举一例。,第1张

1、值传递,创建变量x和y,x的值等于a的值,y的值等于b的值

void Exchg1(int x, int y) 

{

int tmp;

tmp=x;

x=y;

y=tmp;

printf(“x=%d,y=%d/n”,x,y)

}

void main()

{

int a=4,b=6;

Exchg1 (a,b) ;

printf(“a=%d,b=%d/n”,a,b)

}

2、地址传递,相当于建立了px和py两个指向整型的指针,其值分别为a和b的地址

Exchg2(int px, int py)

{

int tmp=px;

px=py;

py=tmp;

print(“px=%d,py=%d/n”,px,py);

}

main()

{

int a=4;

int b=6;

Exchg2(&a,&b);

Print(“a=%d,b=%d/n”, a, b);

}

3、引用传递,x和y直接引用a和b,对a和b *** 作,相当于给a、b起了别名x、y

Exchg2(int &x, int &y)

{

int tmp=x;

x=y;

y=tmp;

print(“x=%d,y=%d/n”,x,y);

}

main()

{

int a=4;

int b=6;

Exchg2(a,b);

Print(“a=%d,b=%d/n”, a, b);

}

扩展资料:

printf用法:

printf()函数的调用格式为:printf("<格式化字符串>",<参量表>)。

其中格式化字符串包括两部分内容:一部分是正常字符,这些字符将按原样输出;另一部分是格式化规定字符,以"%"开始,后跟一个或几个规定字符,用来确定输出内容格式。

参量表是需要输出的一系列参数,其个数必须与格式化字符串所说明的输出参数个数一样多,各参数之间用","分开,且顺序一一对应,否则将会出现意想不到的错误。

比如:

int a=1234;

printf("a=%d\n",a);

输出结果为a=1234。

通常在使用VC进行函数定义时会指定该函数调用方式,诸如:

int __stdcall max(int a, int b)

{

return aba:b;

}

int __cdecl min(int a, int b)

{

return aba:b;

}

bool __fastcall equal(int a, int b)

{

return a=btrue:false;

}

首先,让我们来分个类,调用方法分为两大类另加一个较特殊的__thiscall

第一类:__stdcall类 别名:WINAPI,CALLBACK,PASCAL。该类特点是:主调函数负责参数入栈,由函数本身负责栈的恢复。

第二类:__cdecl类 别名:C/C++中默认调用方式,若你定义函数未指定函数调用约定(Calling Conventions),例如在VC6中下面两个函数的调用约定是等价的:

int max(int a, int b)

{

return aba:b;

}

int __cdecl min(int a, int b)

{

return aba:b;

}

该类调用约定的特点是:由主调函数负责参数入栈,并由主调函数负责线的恢复。

第三类:__thiscall 该类比较特殊,只用于类成员函数调用,你甚至不能强制指定这个函数调用约定。它是由C/C++编译器自动添加的。在C/C++中类成员函数会默认传入一个this指针,对于此,在默入情况下,C/C++中类成员函数通过此类调用约定来指定this指针。

接着介绍一下__thiscall,__thiscall是关于类的一种调用方式。

它与其他调用方式的最大区别是:

__thiscall对每个函数都增加了一个类指针参数

class aa

{

void bb(int cc);

};

实际上bb的函数原形是void bb(aa this, int cc);

__cdecl的调用方式介绍: C和C++缺省调用方式

例子:

void Input( int m,int n);/相当于void __cdecl Input(int m,int n);/

以下是相应的汇编代码:

00401068 lea eax,[ebp-8] ;取[ebp-8]地址(ebp-8),存到eax

0040106B push eax ;然后压栈

0040106C lea ecx,[ebp-4] ;取[ebp-4]地址(ebp-4),存到ecx

0040106F push ecx ;然后压栈

00401070 call @ILT+5(Input) (0040100a);然后调用Input函数

00401075 add esp,8 ;恢复栈

从以上调用Input函数的过程可以看出:在调用此函数之前,首先压栈ebp-8,然后压栈ebp-4,然后调用函数Input,最后Input函数调用结束后,利用esp+8恢复栈。由此可见,在C语言调用中默认的函数修饰_cdecl,由主调用函数进行参数压栈并且恢复堆栈。

下面看一下:地址ebp-8和ebp-4是什么? 在VC的VIEW-debug windows-Registers,显示寄存器变量值,然后在选debug windows-Memory,输入ebp-8的值和ebp-4的值(或直接输入ebp-8和-4),看一下这两个地址实际存储的是什么值,实际上是变量"n "的地址(ebp-8),m的地址(ebp-4)。

由此可以看出:在主调用函数中进行实参的压栈并且顺序是从右到左。另外,由于实参是相应的变量的引用,也证明实际上引用传递的是变量的地址(类似指针)。

总结:在C或C++语言调用中默认的函数修饰_cdecl,由主调用函数进行参数压栈并且恢复堆栈,实参的压栈顺序是从右到左,最后由主调函数进行堆栈恢复。由于主调用函数管理堆栈,所以可以实现变参函数。另外,命名修饰方法是在函数前加一个下划 线(_)。

_stdcall调用约定介绍:实际上就是PASCAL,CALLBACK,WINAPI

例子:

void WINAPI Input( int m,int n);

看一下相应调用的汇编代码:

00401068 lea eax,[ebp-8]

0040106B push eax

0040106C lea ecx,[ebp-4]

0040106F push ecx

00401070 call @ILT+5(Input) (0040100a)

从以上调用Input函数的过程可以看出:在调用此函数之前,首先压栈ebp-8,然后压栈ebp-4,然后调用函数Input,在调用函数Input之后,没有相应的堆栈恢复工作(为其它的函数调用,所以我没有列出)下面再列出Input函数本身的汇编代码:(实际此函数不大,但做汇编例子还是大了些,大家可以只看前和后,中间代码与此例子无关)

39: void WINAPI Input( int m,int n)

40: {

00401110 push ebp

00401111 mov ebp,esp

00401113 sub esp,48h

00401116 push ebx

00401117 push esi

00401118 push edi

00401119 lea edi,[ebp-48h]

0040111C mov ecx,12h

00401121 mov eax,0CCCCCCCCh

00401126 rep stos dword ptr [edi]

41: int s,i;

42:

43: while(1)

00401128 mov eax,1

0040112D test eax,eax

0040112F je Input+0C1h (004011d1)

44: {

45: printf("Please input the first number m:");

00401135 push offset string "Please input the first number m" (004260b8)

0040113A call printf (00401530)

0040113F add esp,4

46: scanf("%d",m);

00401142 mov ecx,dword ptr [ebp+8]

00401145 push ecx

00401146 push offset string "%d" (004260b4)

0040114B call scanf (004015f0)

00401150 add esp,8

47:

48: if ( m= s )

004011B3 mov eax,dword ptr [ebp+8]

004011B6 mov ecx,dword ptr [eax]

004011B8 cmp ecx,dword ptr [ebp-4]

004011BB jl Input+0AFh (004011bf)

57: break;

004011BD jmp Input+0C1h (004011d1)

58: else

59: printf(" m

n(n+1)/2,Please input again!");

004011BF push offset string " m

n(n+1)/2,Please input agai" (00426060)

004011C4 call printf (00401530)

004011C9 add esp,4

60: }

004011CC jmp Input+18h (00401128)

61:

62: }

004011D1 pop edi

004011D2 pop esi

004011D3 pop ebx

004011D4 add esp,48h

004011D7 cmp ebp,esp

004011D9 call __chkesp (004015b0)

004011DE mov esp,ebp

004011E0 pop ebp

004011E1 ret 8

之后,我们看到在函数末尾部分,有ret 8,明显是恢复堆栈,由于在32位C++中,变量地址为4个字节(int也为4个字节),所以d栈两个地址即8个字节。由此可以看出:在主调用函数中负责压栈,在被调用函数中负责恢复堆栈。因此不能实现变参函数,因为被调函数不能事先知道d栈数量,但在主调函数中是可以做到的,因为参数数量由主调函数确定。

下面再看一下,ebp-8和ebp-4这两个地址实际存储的是什么值,ebp-8地址存储的是n 的值,ebp -4存储的是m的值。说明也是从右到左压栈,进行参数传递。

总结:_stdcall在主调用函数中负责压栈,在被调用函数中负责d出堆栈中的参数,并且负责恢复堆栈。因此不能实现变参函数,参数传递是从右到左。另外,命名修饰方法是在函数前加一个下划线(_),在函数名后有符号(@),在@后面紧跟参数列表中的参数所占字节数(10进制),如:void Input(int m,int n),被修饰成:_Input@8 对于大多数api函数以及窗口消息处理函数皆用CALLBACK,所以调用前,主调函数会先压栈,然后api函数自己恢复堆栈。

如:

push edx

push edi

push eax

push ebx

call getdlgitemtexta

最后,在SDK中输出API函数的时候,经常会利用WINAPI对函数进行约定,WINAPI在WIN32中,它被定义为__stdcall 函数调用约定有多种,这里简单说一下:

1、__stdcall调用约定相当于16位动态库中经常使用的PASCAL调用约定。在32位的VC++50中PASCAL调用约定不再被支持(实际上它已被定义为__stdcall。除了__pascal 外,__fortran和__syscall也不被支持),取而代之的是__stdcall调用约定。两者实质上是一致的,即函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。

_stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的 压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。

2、C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把参数d出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。

_cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。

3、__fastcall调用约定是“人”如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。

_fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。

4、thiscall仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。

5、naked call采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。

API(Application Programming Interface,应用程序接口)函数是一些预先定义的函数。 *** 作系统除了协调应用程序的执行、内存分配、系统资源管理外。

同时也是一个很大的服务中心,调用这个服务中心的各种服务(每一种服务是一个函数),可以帮助应用程序达到开启视窗、描绘图形、使用周边设备的目的。

例如,图形库中的一组API定义了绘制指针的方式,可于图形输出设备上显示指针。当应用程序需要指针功能时,可在引用、编译时链接到这组API,而运行时就会调用此API的实现(库)来显示指针。

扩展资料

应用:通过API文本查看器,可以方便地查找程序所需要的函数声明、结构类型和常数,然后将它复制到剪贴板,最后再粘贴到VB程序的代码段中。

在大多数情况下,只要确定了程序所需要的函数、结构和常数这三个方面后,就可以通过对API文本游览器的以上 *** 作将他们加入到程序段中,从而程序中可以使用这些函数了。这些是学习API最基本的常识问题,它远远占不到API的庞大的体系内容。

-API函数

代码

Private Type PROCESSENTRY32

dwSize As Long

cntUsage As Long

th32ProcessID As Long

th32DefaultHeapID As Long

th32ModuleID As Long

cntThreads As Long

th32ParentProcessID As Long

pcPriClassBase As Long

dwFlags As Long

szExeFile As String 1024

End Type

Private Type MODULEENTRY32

dwSize As Long

th32ModuleID As Long

th32ProcessID As Long

GlblcntUsage As Long

ProccntUsage As Long

modBaseAddr As Byte

modBaseSize As Long

hModule As Long

szModule As String 256

szExePath As String 1024

End Type

Const TH32CS_SNAPHEAPLIST = &H1

Const TH32CS_SNAPPROCESS = &H2

Const TH32CS_SNAPTHREAD = &H4

Const TH32CS_SNAPMODULE = &H8

Const TH32CS_SNAPALL = (TH32CS_SNAPHEAPLIST Or TH32CS_SNAPPROCESS Or TH32CS_SNAPTHREAD Or TH32CS_SNAPMODULE)

Const TH32CS_INHERIT = &H80000000

Public Const MF_DISABLED = &H2

Public Const MF_BYPOSITION = &H400

Public Const HWND_TOPMOST = -1

Public Const HWND_BOTTOM = 1

Public Const HWND_NOTOPMOST = -2

Public Const SWP_NOSIZE = &H1

Public Const SWP_NOMOVE = &H2

Public Const SWP_NOZORDER = &H4

Public Const SWP_NOACTIVATE = &H10

Public Const SWP_SHOWWINDOW = &H40

Public Const SWP_HIDEWINDOW = &H80

Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long

Private Declare Function Process32First Lib "kernel32" (ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long

Private Declare Function Process32Next Lib "kernel32" (ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long

Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Private Declare Function EnumWindows& Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long)

Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long

Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long

Private Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As Long) As Long

Public Declare Function SetWindowPos Lib "user32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long

Function GetProcessID(EXEName As String, ProcessID As Long) As Boolean

Dim my As PROCESSENTRY32

Dim l As Long

l = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)

If l Then

mydwSize = 1060

If (Process32First(l, my)) Then

Do

If UCase$(Right$(Left$(myszExeFile, InStr(1, myszExeFile, Chr$(0)) - 1), Len(EXEName))) = UCase$(EXEName) Then

CloseHandle l

ProcessID = myth32ProcessID

GetProcessID = True

Exit Function

End If

Loop Until (Process32Next(l, my) < 1)

End If

CloseHandle l

End If

GetProcessID = False

End Function

Private Function EnumWindowsProc(ByVal hwnd As Long, ByVal lParam As Long) As Boolean

Dim lpdwProcessId As Long

GetWindowThreadProcessId hwnd, lpdwProcessId

If lpdwProcessId = lParam Then

If GetParent(hwnd) = 0 And IsWindowVisible(hwnd) Then Form1List1AddItem hwnd

End If

EnumWindowsProc = True

End Function

Public Sub FindProcessWindow(ProcessID As Long)

Call EnumWindows(AddressOf EnumWindowsProc, ProcessID)

End Sub

'窗体代码

Private Sub Command1_Click()

Dim ProcessID As Long

If GetProcessID("qqexe", ProcessID) Then FindProcessWindow ProcessID

End Sub

GetProcessID(EXEName, ProcessID)

找到 文件名为EXEName的进程, ProcessID 返回的进程ID

FindProcessWindow(ProcessID As Long)

找到 属于进程ProcessID 的窗口,可能不只一个,窗口句柄放到了列表框List1中

窗口置前

Call SetWindowPos(Form1hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE Or SWP_SHOWWINDOW Or SWP_NOSIZE Or SWP_NOMOVE)

窗口不置前

Call SetWindowPos(Form1hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE Or SWP_SHOWWINDOW Or SWP_NOSIZE Or SWP_NOMOVE)

 DELPHI的文章已经很多了 大家是不是被它强大的开发功能所折服?可视化编程的出现 使很多朋友圆了程序梦 的确 几个控件的搭配再加上几条语句 也许一个软件就出来了 但是 你是否已不满足使用别人的元件?想更深入的了解windows的编程?更换的掌握DELPHI?OK 请继续往下看

 DOS下的INT H对于开发DOS程序很重要 而对于在WINDOWS下开发程序 了解api函数是很有必要的 元件就像招式 而API函数就好比内功心法 有了高深的内力 那么学习招式就易如反掌啦 所以 掌握的API函数越多 你就越可能成为DELPHI高手 甚至WINDOWS编程高手 所以 无论是C++ VB还是DELPHI都无一例外的支持API函数的调用 C++和DELPHI是全面支持API函数 而VB仅支持部分API函数(部分也不少了 已经有几百条之多) 尤其是DELPHI调用API函数最方便 已经达到与API函数合二为一的境界 调用API函数和使用自身的函数几乎一样 下面我将为大家详细讲解API函数在DELPHI中的使用 每条语句我都会写个小例子 让大家更能明白其是如何使用的 如果你认真看完本文后 相信大家一定会对DELPHI编程有更深的了解 但是千万不要半途而废 畏难怕繁 记住 欲练神功 必下苦功!学编程 可不是玩游戏 不能弄个FPE啥作弊的

 (一)控件与消息函数

 ①语法 AnyPopup BOOL

 单元 windows pas(该单元DELPHI会自行在USES里加上 下同)

 作用 判断屏幕上是否存在任何d出式窗口

 返回值 BOOL 如存在d出式菜单 则返回TRUE

 注解 对该函数来说 d出式菜单包含所有可见的包容顶级窗口 无论d出式还是重叠窗口

 示例

 procedure TForm Button Click(Sender Tobject)

 begin

 if (AnyPopup) then

 Label Caption = Pop-ups found TRUE

 else

 Label Caption = Pop-ups found FALSE

 end

 ②语法 EnableWindow(hWnd HWND bEnable BOOL) BOOL 单元 windows pas

 作用 指定的窗口里允许或禁止所有鼠标及键盘输入

 返回值 BOOL 如果返回真 WINDOWS已经禁止 否则返回假

 示例

 procedure TForm Button Click(Sender TObject)

 begin

 if (IsWindowEnabled(Edit Handle)) then

 begin

 EnableWindow(Edit Handle FALSE)

 Button Caption = Enable Window

 Edit Text = This window is disabled

 end

 else

 begin

 EnableWindow(Edit Handle TRUE)

 Button Caption = Disable Window

 Edit Text = This window is enabled

 end

 end

 ③语法 FlashWindow(hWnd HWND bInvert BOOL) BOOL

 单元 windows pas

 作用 闪烁显示指定窗口 这意味着窗口的标题和说明文字会发生变化 似乎从活动切换到非活动状态 或反向切换 通常对不活动的窗口应用这个函数 引起用户的注意

 返回值 BOOL 如窗口在调用前处于活动状态 则返回TRUE  [Page]

 注解 该函数通常与一个计数器组合使用 生成连续的闪烁效果

 在windows NT及windowsfor workgroup中 bInvert参数会被忽略

 但在windows 中不会忽略

 示例

 procedure TForm Timer Timer(Sender TObject)

 begin

 FlashWindow(Form Handle TRUE)

 FlashWindow(Application handle TRUE)

 end

 ④语法 SetWindowText(hWnd HWND lpString PChar) BOOL

 单元 windows pas

 作用 设置窗口的标题文字或控件的内容

 返回值 设置成功返回TRUE 否则返回FALSE

 示例

 procedure TForm Button Click(Sender TObject)

 var

 TheText PChar

 TextLen Integer

 begin

 TextLen =GetWindowTextLength(Form Handle)

 GetMem(TheText TextLen)

 GetWindowText(Form Handle TheText TextLen+ )

 Edit Text =string(TheText)

 FreeMem(TheText)

 end

 procedure TForm Button Click(Sender TObject)

 begin

 SetWindowText(Form Handle PChar(Edit Text))

 end

 ⑤语法 IsWindow(hWnd HWND) BOOL

 单元 windows pas

 作用 判断一个窗口句柄是否有效

 返回值 有效返回TRUE 否则返回FALSE

 示例

 procedure TForm Button Click(Sender TObject)

 begin

 if (IsWindow(Button Handle)) then

 Button Caption = TRUE

 else

 Button Caption = FALSE

 end

 怎么样 还过瘾吧?今天是第一次 就介绍些较容易接受的函数 否则朋友们肯定会喊吃不消 不知道朋友们对这样的编排形式能够接受吗?还有 我会按照API函数的分类(控件与消息函数/硬件与系统函数/菜单函数/文本和字体函数/打印函数等等)分别介绍 但我不会介绍全部的API函数 否则大有骗稿费之嫌疑 而且本人的水平也难做到每个语句都有示例 只介绍平常用得上的 本人经常使用的函数 有时也会介绍一下比较隐秘但却非常有用的API函数

 附tips(DELPHI技巧)一个

 如果有这样一个目录

 c window ediatempabcsoundchime wav

 我希望它能缩短成

 c windows……soundchime wav

 如何写程序呢?

 回答

 用下面的过程试试

 function shortenfilename(s string) string

 var drive curdrive string[ ]

 dir curdir string[ ]

 name string[ ]   [Page]

 ext string[ ]

 i byte

 begin

 for i = to length(s) do s[i] =upcase(s[i])

 s =fexpand(s)

 fsplit(s dir name ext)

 drive =copy(dir )

 dir =copy(dir length(dir)- )

 getdir( curdir)

 curdrive =copy(curdir )

 curdir =copy(curdir length(curdir)- )+

 if drive=curdrive then begin

 if copy(dir length(curdir))=curdir then begin

 i =length(curdir)

 if length(dir)<>i then dir =dir+

 shortenfilename =copy(dir i+ length(dir)-i- )+name+ext

 end else shortenfilename =copy(s length(s)- )

 end else shortenfilename =s

 end

lishixinzhi/Article/program/Delphi/201311/8422

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/12176309.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-21
下一篇 2023-05-21

发表评论

登录后才能评论

评论列表(0条)

保存