函数和函数指针用来控制程序的执行顺序,但是它们可能会被误用,导致不可预期的行为。考虑下面的getSystemStatus
函数的使用,它返回反应系统状态的整数:
int getSystemStatus() {
int status;
...
return status;
}
下面是判断系统状态是否为0的最好方法:
if(getSystemStatus() == 0) {
printf("Status is 0\n");
} else {
printf("Status is not 0\n");
}
接下来这个例子中,忘记了加上括号,这段代码不会正常执行:
if(getSystemStatus == 0) {
printf("Status is 0\n");
} else {
printf("Status is not 0\n");
}
系统会一直执行else
分支,在逻辑表达式中,我们把函数的地址和0作比较,而不是调用函数后比较返回值和0。记住,只用函数名本身时返回的是函数的地址。
一个类似的错误是直接使用函数返回值,而不会将它的结果和其他值进行比较,这样会返回地址然后计算真假,而函数的地址不大可能是0,结果就是返回的地址计算为真,因为C把非0值都作为真:
if(getSystemStatus) {
// 永远为真
}
我们应该像下面这样写函数调用来判断状态是否为0:
if(getSystemStatus()) {
如果函数和函数指针的签名不同,不要把函数赋给函数指针,这样会导致未定义的行为,一个误用的例子如下所示:
int (*fptrCompute)(int,int);
int add(int n1, int n2, int n3) {
return n1+n2+n3;
}
fptrCompute = add;
fptrCompute(2,5);
我们试图只用两个参数调用add
函数,而该函数期望的是三个参数,代码能编译,但是输出是不确定的。
函数指针可以执行不同的函数,这取决于分配给它的地址。比如说,我们可能想为一般的操作使用printf
函数,但是有时候为了打印特定日志需要换成别的函数,那么可以像下面这样声明并使用函数指针:
int (*fptrIndirect)(const char *, ...) = printf;
fptrIndirect("Executing printf indirectly");
攻击者可能用缓冲区溢出来覆写函数指针的地址,如果发生这类攻击,控制可能会转移到内存中的任意位置。