参考文章:[c++] 用宏定义一个函数 - 推杯问盏 - 博客园

内联函数

对于一个频繁使用的短小函数,在C语言中应该用宏定义实现,在C++中用inline实现。宏定义与内联函数的不同用法000。

宏定义

在c语言中,写一手漂亮的宏定义是非常有必要的,方式出错;提高代码的移植性和可读性等。尤其是一些常用或通用的功能函数或者代码段,这些功能既可以写成函数,也可以封装为宏定义。就功能性上来讲,用宏定义自然有函数无法比拟的优势所在。

要点:变量都用括号括起来,防止出错,结尾不需要;。在实际编程中,不推荐把复杂的函数使用宏,不容易调试。多行用\

c
1
#define max(a,b) ((a)>(b)?(a):(b)) //一个简单的大小比较的宏定义函数

但是如果用函数实现的话,你可能得先声明,并且定义,尤其是对变量类型的局限性上:

c
1
2
3
4
5
int max(int a ,int b); //声明
//定义
int max(int a, int b) {
return a>b?a:b;
}

比较明显,宏定义简单又方便;同时,函数地调用会带来额外的开销,他需要开辟一片栈空间,记录返回地址,函数返回还要释放。这种开销很明显地会降低代码效率,而且代码量也会大大增加,而使用宏定义则在代码规模和速度方面都有优势。其次就是变量类型上,在C++中因为有模板还可以实现多类型变量的比较,但是在上述代码中仅仅支持int类型的变量进行比较。但宏定义就有所不同,可以用于整形、长整型、单浮点型、双浮点型以及其他一些可以用>操作符进行比较的变量类型,就是说,宏定义是不需要考虑类型的。

在具体使用中,较多场合会因为使用宏定义重命名函数名以及定义一些变量。

c
1
2
#define MALLOC(n, type) \
((type *) malloc((n)* sizeof(type))

利用这个宏,我们就可以简单的为任何类型分配一段我们指定的空间大小,并返回指向这段空间的指针。

c
1
2
int *ptr;
ptr = MALLOC( 5, int );//申请5int大小的空间

关于宏定义还会有一些小问题,可能也会导致你使用上的错误,例如:

c
1
2
3
4
5
6
7
8
9
10
#define SQUARE_SUM(x,y) x*x+y*y
#include <stdio.h>

int main()
{
int i = 1,j = 2,k ;
k = SQUARE_SUM(i+1,j);
printf("%d",k);
return 0;
}

上述代码真实的调用是1+1*1+1+2*2=7,这一点需要注意,所以如果有需要的话,建议给变量小括号带上。

宏定义小结

属性 #define宏 函数
代码长度 每次使用时,宏代码都被插入到程序中。除了非常小的宏之外,程序的长度将大幅度增长。 函数代码只出现于一个地方:每次使用这个函数时,都调用那个地方的同一份代码
执行速度 更快 存在函数调用、返回的额外开销
操作符优先级 宏参数的求值是在所有周围表达式的上下文环境里,除非它们加上括号,否则邻近操作符的优先级可能产生不可预料的结果。 函数参数只在函数调用时求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。
参数求值 参数用于宏定义时,每次都将重新求值,由于多次求值,具有副作用的参数可能会产生不可预测的结果。 参数在函数调用前只求值一次,在函数中多次使用参数并不会导致多次求值过程,参数的副作用并不会造成任何特殊问题。
参数类型 宏与类型无关,只要参数的操作是合法的,它可以用于任何参数类型。 函数的参数是与类型有关系的,如果参数的类型不同,就需要使用不同的函数,即使它们执行的任务是相同的。

inline函数

在系统下,栈空间是有限的,加入频繁大量的使用就会造成因栈空间不足所造成的程序出错的问题,内联函数的引入也是为了解决一些频繁调用的小函数大量消耗栈空间的问题。

看一个例子:

c++
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>  
//函数定义为inline即:内联函数
inline char* dbtest(int a) {
return (i % 2 > 0) ? "奇" : "偶";
}
int main()
{
int i = 0;
for (i=1; i < 100; i++) {
printf("i:%d 奇偶性:%s /n", i, dbtest(i));
}
}

在这种情况时当在下表调用函数时,会变成(i%2>0)?"奇":"偶";避免了函数的重复调用对栈内存重复开辟所带来的消耗;当然,inline函数不是在任何条件下都可以使用的,只有函数本身没有调用本身并且函数不是复杂函数(当函数中存在while循环或swich语句时为复杂函数,但是不是复杂函数还要看编译器对它的判断)时,才可以使用;


注意点:

inline函数仅仅是一个建议,对编译器的建议,所以最后能否真正内联,看编译器的意思,它如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已。

其次,因为内联函数要在调用点展开,所以编译器必须随处可见内联函数的定义,要不然,就成了非内联函数的调用了.所以,这要求每个调用了内联函数的文件都出现了该内联函数的定义。因此,将内联函数放在头文件里实现是合适的,省却你为每个文件实现一次的麻烦.而所以声明跟定义要一致,其实是指,如果在每个文件里都实现一次该内联函数的话,那么,最好保证每个定义都是一样的,否则,将会引起未定义的行为,即是说,如果不是每个文件里的定义都一样,那么,编译器展开的是哪一个,那要看具体的编译器而定.所以,最好将内联函数定义放在头文件中.
而类中的成员函数缺省都是内联的,如果在类定义时就在类内给出函数,那当然最好;如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上inline,否则就认为不是内联的.
为了方便,将内联函数直接声明时就定义,放在头文件中.这样其它文件包含了该头文件,就在每个文件都出现了内联函数的定义.就可以内联了.