1.宏和函数
宏通常被应用于执行简单的运算。
宏的优点:
1)宏比函数在程序的规模和速度方面更胜一筹。宏是插入到程序中直接执行的,而函数还要建立栈帧和销毁栈帧,这些工作所耗费的时间可能比函数内部的指令的执行所耗费的时间还要多。
2)宏是类型无关的。函数的参数必须声明为特定的类型,所以函数只能在类型合适的表达式上使用。反之宏,只要对参数的操作是合法的,就可以用于任何参数类型。
宏的缺点:
1)增加程序的长度。
2)宏没办法调试。
3)与类型无关,编译时不能做类型检查。
4)宏可能会带来运算符优先级的问题,表达式的求值结果不容易预测。
5)带副作用的宏参数多次使用时,会导致不可预测的后果。
对比宏和函数:
属性 | 宏 | 函数 |
---|---|---|
代码长度 | 宏的代码是直接插入到程序中的,会大幅度增加代码的长度 | 函数在内存空间中只有一份,每次使用时都去调用这一份代码 |
运行速度 | 快 | 调用和销毁函数会耗费时间 |
参数类型 | 只要对参数的操作是合法的,就可以使用任意类型参数 | 函数定义时有特定的参数类型,只能调用相应类型的参数 |
操作符优先级 | 宏代码插入程序中,要结合上下文环境运算,如果宏没有加括号,可能会带来预想不到的后果 | 函数是在函数内部进行求值运算,再将结果返回,表达式求值更直观 |
参数求值 | 带有副作用的宏参数求值可能会产生不可预料的后果 | 参数只在函数被调用前求值一次 |
带有副作用的宏参数求值:
#include <stdio.h>
#define MAX(a,b) ((a)>(b)?(a):(b))
int main(int argc, char** argv) {
int x = 5;
int y = 8;
int z = MAX(x++,y++);
printf("x=%d,y=%d,z=%d\n",x,y,z);
return 0;
}
宏展开:int z = ((x++)>(y++)?(x++):(y++));
较大的那个数会被调用两次++,但这并不是我们的初衷
运行结果:
x=6,y=10,z=9
x=5和y=8比较大小后,x=6,y=9,y在刚刚的比较中胜出,又因为是后置++,因此将y的值赋给z,y+1之后y=10
2.宏和内联函数
内联函数结合了宏和函数的优点:
1)快。内联函数在编译时将函数直接嵌入调用程序的主体,省去调用和返回指令。
2)类型检查。编译器会对内联函数的参数类型做安全检查或者自动类型转换。
3)可调试。内联函数在运行时是可以调试的。
内联函数只适用于:
1)较短的代码
2)函数体内不出现循环和递归
内联函数比宏更安全。
3.指针和引用
1)引用必须初始化,并且不能更改为引用其他变量,而指针是可变的。
2)引用必须指向有效的变量,指针可以为空。
3)sizeof引用时,求的是引用变量的大小,sizeof指针时,求的是指向变量地址的大小。
4)引用++时,只加1,指针++时,加的是类型的大小。
5)引用比指针更安全。
引用内部实现和指针一样,将代码进行反汇编,会发现两者的代码相同。
swap_by_reference(x,y);
0x0000000000401594 <+34>: lea rdx,[rbp-0x8]
0x0000000000401598 <+38>: lea rax,[rbp-0x4]
0x000000000040159c <+42>: mov rcx,rax
0x000000000040159f <+45>: call 0x401530 <swap_by_reference(int&, int&)>
swap_by_point(&x,&y);
0x00000000004015a4 <+50>: lea rdx,[rbp-0x8]
0x00000000004015a8 <+54>: lea rax,[rbp-0x4]
0x00000000004015ac <+58>: mov rcx,rax
0x00000000004015af <+61>: call 0x401564 <swap_by_point(int*, int*)>
两者传参都是先将y的地址赋值给rdx,再将x的地址传给rax。
进入函数内部使用引用时,都会进行解引用取值进行运算。
4.malloc/free和new/delete
1)malloc/free是C/C++标准库的函数,new/delete是C++操作符。
2)malloc/free只是动态分配内存空间/释放空间,而new/delete除了分配空间还会调用构造函数和析构函数进行初始化和清理。
3)malloc/free需要手动计算类型大小,返回值是void*,new/delete可自己计算类型大小,返回对应类型的指针。
4)malloc失败了返回0,new失败了会抛出异常。