在阅读K&R中macro substitution一节中提到
引用
One practical example comes from <stdio.h>, in which getchar and putchar are often defined as macros to avoid the run-time overhead of a
function call per character processed.
在standard input and output一节时,也提到
引用
As we mentioned earlier,"functions" like getchar and putchar in <stdio.h> and tolower in <ctype.h> are often macros, thus avoiding the
overhead of a function call per character.
也就是说使用函数宏可以避免对每个字符都进行函数调用的开销。文中提到的#define也只是起到了文本替换的作用,但没有告知减少开销的原因。想了半天没想明白,后来发现想复杂了,原来这么简单的道理……
用书上的一个例子
#define max(A, B) ((A)>(B)?(A):(B))
x = max(p+q, r+s);
就想#define本身的作用那样,经过preprocessor处理过之后,就成了
x = ((p+q)>(r+s)?(p+q):(r+s));
1、书中也说过,宏定义经过preprocessor做文本上的替换,之后再经过编译器处理,所以函数宏的参数没有类型,因此要自己注意传递参数的类型。
2、真函数调用和函数宏调用编译之后生成的指令也不同。这个很容易理解,至少经过预处理之后的文本含义也不一样。如果max是个真函数,那么在编译之后的代码中调用函数的地方会编译生成传参指令和call指令。如果max是个函数宏,并不会生成传参和call指令,但是每次调用函数宏的地方生成的指令都相当于一个函数体。目标文件相应的也会大许多。
3、宏定义时的书写一定要注意。例如上面例子中的括号是不能省的,这关系到替换之后运算的优先级问题。同样,最外层的括号也不能省,因为赋值运算符的优先级要高一些。省略了最外面的括号,可能在替换之后所表达的意思就截然不一样了。
4、最后一点也是在macro substitution一节中提到的,要注意传递给函数宏的参数是否在执行过程中有多余的运算。上面的例子,max(++a,++b),原意是a、b各自加一次,但是使用函数宏之后的结果是a、b都自加了两次。
分享到:
相关推荐
在探讨宏定义与函数调用的区别时,我们深入解析它们在C语言编程中的特性、应用场景以及潜在风险,以便更全面地理解这两种编程机制。 ### 宏定义与函数调用的本质区别 #### 宏定义(#define) 宏定义是预处理器的一...
函数宏,即包含多条语句的宏定义,其通常为某一被频繁调用的功能的语句封装,且不想通过函数方式封装来降低额外的弹栈压栈开销。 函数宏本质上为宏,可以直接进行定义,例如: 复制 #define INT_SWAP(a,b) \ int ...
宏交换的优点是执行效率高,因为它在编译时就已经完成了交换,不需要额外的函数调用开销。但需要注意的是,由于宏是简单的文本替换,如果直接使用`EXCHANGE(*x, *y, *temp)`,则会导致错误,因为在宏展开时会尝试对`...
递归函数在计算阶乘时需要注意其效率,因为频繁的函数调用会增加开销。在实际编程中,可以考虑使用循环或其他优化方法,比如记忆化搜索(存储之前计算过的阶乘值,避免重复计算),以提高效率。不过,对于小的整数,...
1. 频繁的函数调用会增加程序的运行时间,因为每次调用都有堆栈操作和参数传递的开销。 2. 函数的参数和返回值都需要占用内存,应合理设计以减少资源消耗。 3. 为了避免无限递归,必须确保所有递归调用都有终止...
当我们谈论“二进制讲解函数调用”时,实际上是在探讨在处理器级别上,如何通过汇编语言实现函数的调用与返回。下面将深入解析这个过程。 首先,我们要明白,函数调用涉及到的主要组件有:函数地址、参数传递、局部...
内联函数的主要目的是为了提高程序的执行效率,通过避免函数调用时的开销,如参数传递、函数调用的返回过程等。而普通函数则遵循标准的调用/返回机制,这在处理大型或复杂功能时是必要的,但在频繁调用的小型函数中...
虚函数调用虽然提供了多态性的强大功能,但也可能带来一定的性能开销。每次虚函数调用都需要通过 vptr 查找对应的函数地址,这增加了额外的间接性。然而,在大多数现代处理器上,这种开销相对较小,并且通常可以通过...
然而,需要注意的是,由于涉及到跨语言调用,可能会有性能开销,因此这种方法更适合处理计算密集型任务,而非I/O操作或其他轻量级任务。 混合编程还涉及到数据类型的转换、错误处理、内存管理等多个方面,都需要...
与宏不同,内联函数在处理参数时会进行实际的计算,而不是简单的文本替换。因此,内联函数能提供类型安全,避免了宏的一些常见陷阱,如未加括号的参数导致的错误。同时,内联函数可以重载,允许根据参数类型的不同...
递归可以使代码简洁明了,但需要注意其额外的开销,包括函数调用的栈空间和计算时间。 5. **生活中的递归实例**:递归的概念在生活中也有体现,比如上面提到的老和尚讲故事的例子,故事内容不断自我引用,形成一个...
它包含了所有用于与Python解释器交互的函数和数据结构。在C代码中包含`Python.h`后,我们可以创建一个Python解释器环境,并执行Python代码或者调用Python函数。 1. **初始化Python解释器**: 在C代码中,首先需要...
### Oracle函数调用存储过程详解 #### 背景与目的 在开发Oracle应用程序时,经常需要使用到存储过程和函数。这两种类型的数据库对象各有优势,可以满足不同的业务需求。有时候,为了更好地组织代码和提高复用性,...
- 函数在运行时调用,有函数调用开销,但代码体积相对较小。 - 函数支持函数指针,允许动态调用,宏则不能。 - 函数有重入性问题,多任务环境需考虑线程安全。 - 编译器对函数参数进行类型检查,提供更安全的...
3. 这种约定适用于大量调用和参数传递的情况,因为它能减少函数调用时的开销。 在Windows API中,函数声明通常会使用` WINAPI`关键字,如示例所示: ```cpp typedef int (WINAPI *PMESSAGE)(); ``` 这里定义了一个...
内联函数是一种优化技术,用于避免函数调用时的开销。通过在函数定义前加上`inline`关键字,可以请求编译器尝试将函数体插入到每个调用处。这可以提高效率,但不是所有情况都适用,因为内联可能会增加代码大小。 ...
内联函数是为了优化代码,通过在每个调用点插入函数体来减少函数调用的开销。但过度使用可能导致代码膨胀。 十二、友元函数和友元类 友元函数可以访问类的私有和保护成员,而友元类的所有成员函数都是类的友元。...
在C++编程语言中,内联函数的引入主要与宏函数的缺陷有关。宏函数是C++中的一个预处理器特性,允许程序员定义可替换的文本片段。然而,宏函数在某些情况下存在一些固有问题,这使得C++引入了内联函数作为更安全、更...
它的主要作用是在编译期间将函数体插入到每个调用该函数的地方,从而避免了函数调用时的开销,如函数调用的压栈、跳转以及返回等过程。在某些情况下,内联函数可以显著提升程序执行速度,尤其是在循环或者频繁调用的...