MSDN帮助文档中关于内联函数学习笔记

--------------------------Naked Function Calls关键字在MSDN中--------------------------------------------
Microsoft Specific
Functions declared with the naked attribute are emitted without prolog or epilog code, enabling you to write your own custom prolog/epilog sequences using the inline assembler. Naked functions are provided as an advanced feature. They enable you to declare a function that is being called from a context other than C/C++, and thus make different assumptions about where parameters are, or which registers are preserved. Examples include routines such as interrupt handlers. This feature is particularly useful for writers of virtual device drivers (VxDs). 


实例函数原型:
__declspec( naked ) int func( int a, int b )
{
__asm ret
}


--------------------------Considerations for Writing Prolog/Epilog Code--------------------------
Microsoft Specific
Before writing your own prolog and epilog code sequences, it is important to understand how the stack frame is laid out. It is also useful to know how to use the __LOCAL_SIZE symbol.


Stack Frame Layout
push        ebp                ; Save ebp
mov         ebp, esp           ; Set stack frame pointer
sub         esp, localbytes    ; Allocate space for locals
push        <registers>        ; Save registers


The localbytes variable represents the number of bytes needed on the stack for local variables, and the <registers> variable is a placeholder that represents the list of registers to be saved on the stack. After pushing the registers, you can place any other appropriate data on the stack. The following is the corresponding epilog code:




pop         <registers>   ; Restore registers
mov         esp, ebp      ; Restore stack pointer
pop         ebp           ; Restore ebp
ret                       ; Return from function


The stack always grows down (from high to low memory addresses). The base pointer (ebp) points to the pushed value of ebp. The locals area begins at ebp-2. To access local variables, calculate an offset from ebp by subtracting the appropriate value from ebp.


__LOCAL_SIZE
The compiler provides a symbol, __LOCAL_SIZE, for use in the inline assembler block of function prolog code. This symbol is used to allocate space for local variables on the stack frame in custom prolog code.


The compiler determines the value of __LOCAL_SIZE. Its value is the total number of bytes of all user-defined local variables and compiler-generated temporary variables. __LOCAL_SIZE can be used only as an immediate operand; it cannot be used in an expression. You must not change or redefine the value of this symbol. For example:


mov        eax, __LOCAL_SIZE           ;Immediate operand--Okay
mov        eax, [ebp - __LOCAL_SIZE]   ;Error


The following example of a naked function containing custom prolog and epilog sequences uses the __LOCAL_SIZE symbol in the prolog sequence:


// the__local_size_symbol.cpp
// processor: x86
__declspec ( naked ) int main() {
   int i;
   int j;


   __asm {      /* prolog */
      push   ebp
      mov      ebp, esp
      sub      esp, __LOCAL_SIZE
      }
      
   /* Function body */
   __asm {   /* epilog */
      mov      esp, ebp
      pop      ebp
      ret
      }
}


------------------------__declspec keyword关键字在MSDN中----------------------------------------------
The extended attribute syntax for specifying storage-class information uses the __declspec keyword, which specifies that an instance of a given type is to be stored with a Microsoft-specific storage-class attribute listed below. Examples of other storage-class modifiers include the static and extern keywords. However, these keywords are part of the ANSI specification of the C and C++ languages, and as such are not covered by extended attribute syntax. The extended attribute syntax simplifies and standardizes Microsoft-specific extensions to the C and C++ languages.


decl-specifier: 
__declspec ( extended-decl-modifier-seq )


extended-decl-modifier-seq: 
extended-decl-modifieropt


extended-decl-modifier extended-decl-modifier-seq


extended-decl-modifier: 
align(#) 


allocate("segname") 


appdomain 


deprecated 


dllimport 


dllexport 


jitintrinsic


naked 


noalias 


noinline 


noreturn 


nothrow 


novtable 


process 


property({get=get_func_name|,put=put_func_name}) 


restrict 


selectany 


thread 


uuid("ComObjectGUID")


White space separates the declaration modifier sequence. Examples appear in later sections.


Extended attribute grammar supports these Microsoft-specific storage-class attributes: align, allocate, appdomain, deprecated, dllexport, dllimport, jitintrinsic, naked, noalias, noinline, noreturn, nothrow, novtable, process, restrict, selectany, and thread. It also supports these COM-object attributes: property and uuid. 


The dllexport, dllimport, naked, noalias, nothrow, property, restrict, selectany, thread, and uuid storage-class attributes are properties only of the declaration of the object or function to which they are applied. The thread attribute affects data and objects only. The naked attribute affects functions only. The dllimport and dllexport attributes affect functions, data, and objects. The property, selectany, and uuid attributes affect COM objects.


The __declspec keywords should be placed at the beginning of a simple declaration. The compiler ignores, without warning, any __declspec keywords placed after * or & and in front of the variable identifier in a declaration.


A __declspec attribute specified in the beginning of a user-defined type declaration applies to the variable of that type. For example:


  Copy Code 
__declspec(dllimport) class X {} varX;
 


In this case, the attribute applies to varX. A __declspec attribute placed after the class or struct keyword applies to the user-defined type. For example:


  Copy Code 
class __declspec(dllimport) X {};
 


In this case, the attribute applies to X.


The general guideline for using the __declspec attribute for simple declarations is as follows:


  Copy Code 
decl-specifier-seqdeclarator-list;
 


The decl-specifier-seq should contain, among other things, a base type (e.g. int, float, a typedef, or a class name), a storage class (e.g. static, extern), or the __declspec extension. The init-declarator-list should contain, among other things, the pointer part of declarations. For example:


  Copy Code 
__declspec(selectany) int * pi1 = 0;   //OK, selectany & int both part of decl-specifier
int __declspec(selectany) * pi2 = 0;   //OK, selectany & int both part of decl-specifier
int * __declspec(selectany) pi3 = 0;   //ERROR, selectany is not part of a declarator
 


The following code declares an integer thread local variable and initializes it with a value:


  Copy Code 
// Example of the __declspec keyword
__declspec( thread ) int tls_i = 1;



----------------------Calling C Functions in Inline Assembly内联汇编中调用C库函数---------Inline Assembler关键字---------

#include <stdio.h>
#include <Windows.h>


char format[] = "%s %s\n";
char hello[] = "Hello";
char world[] = "world";


//在内嵌汇编中调用C库函数
int __declspec(naked) main( void )
{
__asm
{
push offset world
push offset hello
push offset format
call dword ptr printf
add esp,0ch


call dword ptr getchar


xor eax,eax
push eax
push offset world
push offset hello
push eax
call dword ptr MessageBox


ret
}

}


说明:内联汇编的使用会影响编译器对代码的优化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 下面是一个关于 C 内联函数的代码示例: ``` #include <stdio.h> inline int max(int a, int b) { return (a > b) ? a : b; } int main() { int x = 100, y = 200; int z = max(x, y); printf("最大值:%d\n", z); return 0; } ``` 该代码,我们定义了一个内联函数 `max`,用于比较两个整数并返回最大值。在 `main` 函数,我们调用了该内联函数,并将返回值赋给变量 `z`。最后,我们使用 `printf` 函数输出最大值。 ### 回答2: 在C语言内联函数是一种编译器提供的优化方式,它将函数的代码嵌入到调用处,从而减少了函数调用的开销。下面是一个关于C语言内联函数的引用的代码示例: ```c #include <stdio.h> // 定义一个内联函数 inline int add(int a, int b) { return a + b; } int main() { int num1 = 10; int num2 = 20; // 调用内联函数,并将结果赋值给变量sum int sum = add(num1, num2); printf("The sum is: %d\n", sum); return 0; } ``` 在上述代码,我们定义了一个内联函数`add`,它接受两个整数参数并返回它们的和。在`main`函数,我们声明了两个整数变量`num1`和`num2`,并将它们作为参数传递给内联函数`add`,将返回的结果赋值给变量`sum`。最后,我们使用`printf`函数输出变量`sum`的值。 在编译时,编译器会根据内联函数的定义,将函数的代码嵌入到调用处,避免了函数调用的开销,提高了程序的执行效率。 ### 回答3: 内联函数是指在调用处展开执行的函数,它的目的是为了提高函数调用的效率。在C语言,使用关键字inline来定义内联函数,以便在编译时将函数调用直接替换为函数体,从而减少函数调用的开销。 下面是一个关于C语言内联函数的引用的示例代码: ```c #include <stdio.h> // 定义一个内联函数add,用于求两个整数的和 inline int add(int a, int b) { return a + b; } int main() { int num1 = 10; int num2 = 20; // 直接调用内联函数add,会将其展开为a + b的表达式 int sum = add(num1, num2); printf("The sum of %d and %d is %d\n", num1, num2, sum); return 0; } ``` 在上面的代码,我们定义了一个内联函数add,用于求两个整数的和。在main函数,我们调用了add函数来计算num1和num2的和,并将结果赋值给sum变量。由于add函数是内联函数,编译器会将其展开为表达式num1 + num2,从而避免了函数调用的开销。最终,我们使用printf函数将计算结果输出到控制台。 需要注意的是,并非所有的函数都适合作为内联函数。通常情况下,内联函数适合用于简单的、频繁调用的函数,这样可以减少函数调用的开销。而对于复杂的函数或者函数体较大的函数,则不适合使用内联函数。此外,内联函数的定义位置通常应该放在其第一次调用之前,以便编译器能够正确展开函数调用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值