C语言函数使用实例
编写一个判断素数的例子
素数:质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数。
(1)使用函数的形式完成。编写一个函数,主要功能判断啊是否为素数,返回一个布尔值
bool IsPrime(int num){
循环判断素数
返回布尔值
}
(1)大于1的自然数,除了1和它本身外,不能被其他自然数整除。可以通过循环,判断余数是否为0进行判断。
(1)通过返回值的真假,判定素数
(1)main函数是程序入口,当输入数值为6,执行main函数中的if语句,此时if中调用了IsPrime函数,执行IsPrime函数。
(2)执行IsPrime函数,此时num=6,for循环中i=2,i<num成立,执行循环体中的语句。num%i==0,6%2==0,因此退出循环。
(3)执行if语句,num=6,i=2,num==i不成立,执行else,返回值为false。此时IsPrime函数执行完毕。
(4)IsPrime函数返回值是false,执行main函数中的else,因此打印6不是素数。
(1)输入值为7时,执行IsPrime函数中for循环
(2)i=2,i<num,2<7成立,执行num%i,7%2不为0,此时第1次循环结束,执行i++,即i=3。
(3)依次类推,当i=6时,6<7成立,num%i不为0,i值自增为7,判断7<7不成立,循环结束,注意此时i=7。
(4)执行IsPrime中的if语句,num==i,7==7,因此返回值为true。
(5)执行main函数,打印出7是素数。
C/C++学习笔记:你必须学会的5种C/C++函数调用的方式?
本篇文章主要是对C/C++函数调用的几种方式进行了详细的总结介绍,需要的朋友可以过来参考下,希望对大家有所帮助。
调用函数时,计算机常用栈来存储传递给函数的参数。
栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入数据,这个操作被称为压栈(Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修改。用户也可以从堆栈中取走栈顶,称为弹出栈(pop),弹出栈后,栈顶下的一个元素变成栈顶,栈顶指针随之修改。函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。
在参数传递中,有两个重要的问题必须要明确说明:
1. 当参数个数多于一个时,按照什么顺序把参数压入堆栈;
2. 函数调用后,由谁来把堆栈恢复原状。
在高级语言中,就是通过函数的调用方式来说明这两个问题的。
下面就分别介绍这几种调用方式:
1. stdcall
stdcall调用方式又被称为Pascal调用方式。在Microsoft C++系列的C/C++编译器中,使用PASCAL宏,WINAPI宏和CALLBACK宏来指定函数的调用方式为stdcall。
stdcall调用方式的函数声明为:
int _stdcall function(int a, int b);
stdcall的调用方式意味着:
(1) 参数从右向左一次压入堆栈
(2) 由被调用函数自己来恢复堆栈
(3) 函数名自动加前导下划线,后面紧跟着一个@,其后紧跟着参数的尺寸
上面那个函数翻译成汇编语言将变成:
push b 先压入第二个参数
push a 再压入第一个参数
call function 调用函数
在编译时,此函数的名字被翻译为_function@8
2. cdecl
cdecl调用方式又称为C调用方式,是C语言缺省的调用方式,它的语法为:
int function(int a, int b) // 不加修饰符就是C调用方式
int _cdecl function(int a, int b) // 明确指定用C调用方式
cdecl的调用方式决定了:
(1) 参数从右向左依次压入堆栈
(2) 由调用者恢复堆栈
(3) 函数名自动加前导下划线
由于是由调用者来恢复堆栈,因此C调用方式允许函数的参数个数是不固定的,这是C语言的一大特色。
此方式的函数被翻译为:
push b // 先压入第二个参数
push a // 在压入第一个参数
call funtion // 调用函数
add esp, 8 // 清理堆栈
在编译时,此方式的函数被翻译成:_function
3. fastcall
fastcall 按照名字上理解就可以知道,它是一种快速调用方式。此方式的函数的第一个和第二个DWORD参数通过ecx和edx传递,
后面的参数从右向左的顺序压入栈。
被调用函数清理堆栈。
函数名修个规则同stdcall
其声明语法为:
int fastcall function(int a, int b);
4. thiscalll
thiscall 调用方式是唯一一种不能显示指定的修饰符。它是c++类成员函数缺省的调用方式。由于成员函数调用还有一个this指针,因此必须用这种特殊的调用方式。
thiscall调用方式意味着:
参数从右向左压入栈。
如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压入栈后被压入栈。
参数个数不定的,由调用者清理堆栈,否则由函数自己清理堆栈。
可以看到,对于参数个数固定的情况,它类似于stdcall,不定时则类似于cdecl。
5. naked call
是一种比较少见的调用方式,一般高级程序设计语言中不常见。
函数的声明调用方式和实际调用方式必须一致,必然编译器会产生混乱。
函数名字修改规则:
1. C编译时函数名修饰约定规则:
__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_function@8。
__cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_function。
__fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@function@8。
它们均不改变输出函数名中的字符大小写,这和PASCAL调用约定不同,PASCAL约定输出的函数名无任何修饰且全部大写。
2. C++编译时函数名修饰约定规则:
__stdcall调用约定:
(1)以“?”标识函数名的开始,后跟函数名;
(2)函数名后面以“@@YG”标识参数表的开始,后跟参数表;
(3)参数表以代号表示:
X–void ,
D–char,
E–unsigned char,
F–short,
H–int,
I–unsigned int,
J–long,
K–unsigned long,
M–float,
N–double,
_N–bool,
….
PA–表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复;
(4)参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
(5)参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。
其格式为“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,例如:
int Test1(char *var1,unsigned long)—–“?Test1@@YGHPADK@Z”
void Test2() —–“?Test2@@YGXXZ”
__cdecl调用约定:
规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YA”。
__fastcall调用约定:
规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YI”。
VC++对函数的省缺声明是\”__cedcl\”,将只能被C/C++调用。
今天的分享就到这里了,有什么问题的地方欢迎大家指出。
C|函数的调用与返回,本地与非本地跳转
函数是程序的基本构件。函数可由三种单一入口和出口的基本控制结构(顺序、选择、循环)组成,函数内部也可以通过goto实现局部跳转,函数之间能够通过栈机制实现函数调用和返回,类似的,通过适当的语法机制来保存上下文环境,函数之间也能实现非局部跳转。
主调函数调用被调函数,流程控制从主调函数跳转到被调函数之前,上下文环境保存在栈帧上,被调函数执行完后,返回到被调函数的调用点之后:
我们知道,汇编语言可以实现条件或无条件跳转,在高级语言中,goto语句也可以跳转。在结构体编程语言中,程序块由顺序、选择、循环三种结构取代。
The tools provided through the header file setjmp.h allow the programmer to bypass the normal function call and return discipline, by providing the means to perform jumps preserving the calling environment.
通过此头文件setjmp.h提供的工具,程序员可以通过提供执行跳转的方法来保留调用环境,从而绕过正常的函数调用和返回规程。
The header provides, a function, a macro with functional form and a specific type:
头文件提供函数、宏以及函数形式和特定类型:
3.1 setjmp()
在特定的需要函数的返回点调用setjmp即可以保存该处的上下文环境(jmp_buf):
This macro with functional form fills env with information about the current state of the calling environment in that point of code execution, so that it can be restored by a later call to longjmp.
这个函数形式的宏向env填充了关于代码执行点中调用环境的当前状态的信息,以便稍后调用longjmp时可以恢复。
Calling longjmp with the information stored in env restores this same state and returns the control to that same point (the call to setjmp), which is evaluated as a particular non-zero value.
使用存储在env中的信息调用longjmp会恢复相同的状态,并将控制返回到相同的点(对setjmp的调用),该点被计算为特定的非零值。
The state of the calling environment includes the values of all accessible objects, except those of automatic duration local to the function which do not have volatile-qualified types and which change before the call to longjmp; these have indeterminate values.
调用环境的状态包括所有可访问对象的值,但函数本地的自动持续时间对象除外,这些对象没有易失性限定类型,并且在调用longjmp之前更改;这些值不确定。
The invocation of setjmp shall be an expression statement by itself, or be evaluated in a selection or iteration statement either as the (potentially negated) entire controlling expression or compared against an integer constant expression. Otherwise, it causes undefined behavior.
setjmp的调用本身应该是一个表达式语句,或者在选择或迭代语句中作为(可能被否定的)整个控制表达式进行计算,或者与整数常量表达式进行比较。否则,会导致未定义的行为。Restores the environment to the state indicated by env, evaluating the setjmp expression that filled env as val.
3.2 longjmp
将环境恢复到env指示的状态,并将填充env的setjmp表达式计算为val。
The called function containing the longjmp() macro never returns to the point where it has been invoked. Instead, the function transfers the control to the point where setjmp was last used to fill the env, and evaluates the whole expression as val (unless this is zero, in which case it evaluates as value of 1).
包含longjmp()宏的被调函数永远不会返回到调用它的位置。相反,该函数将控件传输到上次使用setjmp填充env的位置,并将整个表达式计算为val(除非这是零,在这种情况下,它计算为值1)。
If env was not filled by a previous call to setjmp or if the function with such call has terminated execution, it causes undefined behavior.
如果之前对setjmp的调用未填充env,或者具有此类调用的函数已终止执行,则会导致未定义的行为。
In C++, the implementation may perform stack unwinding that destroys objects with automatic duration. If this invokes any non-trivial destructors, it causes undefined behavior.
在C++中,实现可以执行堆栈展开,以自动持续时间销毁对象。如果这调用任何非平凡的析构函数,则会导致未定义的行为。
demo code:
-End-
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。