1
三、内联函数 (inline function)
四、引用和数值传递方式具体比较
2
三、内联函数 (inline function)
C++(C99)新增的关键字 inline修饰的函数称为内联函数。内联函数的语法格式与普通函数一样,只是在函数原型或函数定义的标题头之前加上关键字 inline。格式为:
inline type function (type1 parm1,type1 parm2,..,
typen parmn){语句序列 ;}
关键字 inline 通知编译器用内联函数体中的函数代码在虚实结合的时候取代函数调用,调用一次就替换一次。这种替换经过参量之间类型匹配合适性的检查。
内联函数虚实结合处发生的替换称为内联展开,内联展开的结果是程序代码的扩大但执行的时间减少从而效率趋快。
3
关键字 inline是对编译器的优化建议,通常只是对于短小的函数才执行了实际的函数体代码替换。
内联函数力图减少函数调用的堆栈空间之间的调转损耗。递归函数、可变参量的函数以及涉及到复杂指针运算的函数难以内联展开。
编译器对于 inline展开采取复杂的指标,一旦满足这些指标,真实的扩展在函数的调用点进行。为便于内联函数的真实展开,inline函数的定义一般应放置在调用点之前。
C++引入 inline函数的原因一是破除 private或 protected
关键字导致的封装弊端,二是减少宏展开的副作用。宏展开是由 #define预处理指令引入的代码重用的机制。
4
[例 ] swap函数仅交换函数 [例 ] 内层块交换内层局部局部变量的值 变量的值
#include <stdio.h> #include <stdio.h>
inline void swap (int x,int y) void main (void)
{ int t=x; x=y; y=t; { int a=1,b=2;
printf ("%d,%d;",x,y); } { int x=a; int y =b;
void main (void) int t=x; x=y; y=t;
{ int a=1,b=2; printf ("%d,%d;",x,y);}
swap (a,b); // 内层块是 swap(a,b)的内联展开
printf ("%d,%d\n",a,b); } printf ("%d,%d\n",a,b); }
输出结果都为 2,1;1,2,可见函数体中形参的变化不影响实参变量。
5
下面两个程序中的 swap函数交换间接变量的值都输出
1,2 2,1
[例 ] 指针的数值形参交换间接变量
#include <stdio.h>
inline void swap (int *x,int *y)
{ printf ("%d,%d",*x,*y);
int t=*x; *x=*y;*y=t; }
void main (void)
{ int a=1,b=2;
swap (&a,&b);
printf ("%d,%d\",a,b);
}
6
[例 ] 内层块交换间接变量的值
#include <stdio.h>
void main(void)// 左边代码的内联展开
{ int a=1,b=2;
{ int *y =&b;
int* x=&a;
printf ("%d,%d ",*x,*y);
int t=*x; *x=*y; *y=t;
} //内层块是 swap(&a,&b)的内联展开
printf ("%d,%d",a,b);
}
7
考虑到 *(&Lvalue)= Lvalue,上面程序的 swap函数调用具体运算步骤可理解为,
函数调用 swap(&a,&b) 触发虚实结合:
int* x=&a;int *y=&b;
进入函数体:
{ int t= *x; *x= *y; *y= t; }
带实参值参入运算:
x=&a;y=&b;
{ t= *(&a) ; *(&a) = *(&b) ; *(&b) = t }
{ t= a ; a = b ; b = t }
运算完毕从被调函数返回。
8
在函数调用时,指针 x指向变量 a,*x是变量 a的间接变量。
对于间接变量 *x的操作就是对于变量 a的等价操作。指针 y指向变量 b,*y是变量 b的间接变量。
对于间接变量 *y的操作就是对于变量 b的等价操作。通过访问指针形参可以改变主控函数变量的值。
9
四、引用和数值传递方式具体比较在 C++中参数传递方式分为两种:数值传递方式,引用传递方式。差别在于:
引用形参可改变实参变量,数值形参不改变同类型或同级别的实参变量。但访问指针数值形参可以改变指针形参指向的主控函数的变量,这是间接改变。
数值形参对应右值实参,从实参获得初值。引用形参匹配左值实参。左值可作为右值,右值不作为左值。
10
[例 ] 函数 f返回 n的立方,引用形参 x计算正方形周长,访问指针 *p得到正方形面积
#include <stdio.h>
// 函数调用 z=f(x,&y,3)导致 x=4*3,*(&y)=3*3,z=3*3*3
int f (int & r,int*p,int n)
//引用形参 r匹配左值 x,指针数值形参 p匹配右值地址 &y
{ r=4*n; *p=n*n; return n*n*n; }
//变量数值形参 n匹配右值 3
void main()
{ int x,y,z; z=f (x,&y,3);
printf ("%d,%d,%d\n",x,y,z);
} //输出,12,9,27
11
[例 ] 交换变量值的函数。 以下三个程序运行都输出,1,2 ;2,1
[1] 指针的数值形参版本
#include <stdio.h>
void swap (int *x,int *y)
{ printf ("%d,%d;",*x,*y);
int t=*x; *x=*y;*y=t;
}
void main (void)
{ int a=1,b=2;
swap (&a,&b);
printf ("%d,%d",a,b);
}
12
[2] 变量的引用形参版本
#include <stdio.h>
void swap (int &x,int &y)
{ printf ("%d,%d;",x,y);
int t=x; x=y; y=t;
}
void main (void)
{ int a=1,b=2;
swap(a,b);
printf ("%d,%d",a,b);
}
13
[3] 相当于引用版本的内联展开
#include <stdio.h>
void main (void)
{ int a=1,b=2;
{ int& y =b;
int& x=a;
printf ("%d,%d;",x,y);
int t=x; x=y; y=t;
} // swap(a,b)的内联展开
printf ("%d,%d",a,b);
}
14
指针的传值调用与变量的引用调用在上面的例题中具有相同的作用,站在汇编语言的角度看实际上两者是等价的。
变量的引用传递其数据的流通机制本质上同等于指针固定寻址的数值调用,根据兼容 C的 CFRONT策略变量的引用形参版本幕后转换为指针的数值形参版本。
严格地引用形参版本 void swap(int &x,int &y){ int t=x;
x=y; y=t;}概念上对应或映射指针的固定寻址版本即
void swap(int *const x,int
*const y){ int t=*x; *x=*y;*y=t; }
15
上面前两个版本执行文件的大小是一样的。
表面不同的是,指针的传值调用中 x是指针,作为指针的 x可以在程序段中指向不同的变量,函数中采用明朗的间接访问指针形式。
变量的引用传递中 x在一个程序段中仅与一个变量相关联,x隐约地对应相关变量的地址,采用的是变量名的语法。
注意:
void f (int&)与 void f (int*)在实参的匹配上是不同的。
void f(int&)匹配 int型的左值,void f(int*)匹配 int*型的地址表达式 (右值 )。
16
[例 ]求函数极大值
[1] double*型指针输入和指针返回
#include<stdio.h>
double* pmax(double* x,double* y)
{ if (*x>*y) return x;
else return y;
}
void main (void)
{ double x=100,y=0;
*pmax (&x,&y)+=100;
printf ("x=%f,y=%f\n",x,y);
} //程序输出,x=200.000000,y=0.000000
17
[例 ]求函数极大值
[2] double&型引用输入和引用返回
#include<stdio.h>
double& rmax (double& x,double& y)
{ if (x>y) return x;
else return y;
}
void main (void)
{ double x=100,y=0;
rmax (x,y)+=100;
printf ("x=%f,y=%f\n",x,y);
}
//程序输出,x=200.000000,y=0.000000
18
说明:
[1]例题是双重传值,函数的输入传值,函数的返回也是传值,操作的变量是 double*型的指针。 pmax (&x,&y)
是 double*型的右值,*pmax(&x,&y)是 double型的左值。
[2]例题是函数的引用输入与引用返回。两者实现相同的功能,但引用语法略微简洁。根据兼容 C的 CFRONT策略,右边的代码在幕后处理为左边的代码,然后转换汇编语言代码。
pmax返回入口形参的指针,实质上返回的是相应实参的地址,这个实参的地址指向主控函数的局部变量的内存区。变量的引用返回几乎相当于指针的数值返回。
19
与引用相关的变量的生存期对于主控程序应是可访问的。确保返回变量引用的函数和返回指针数值的函数操作的数据的生存期对于主控函数是存在的是有效的。
引用是已经存在的变量的别名,是不独立存在的变量。
引用的值是存储单元中的内容即相关变量的值。对于字符型数据,变量和引用的值占 1字节的单元,对于双精度型的数据,占 8字节的单元。
对引用的操作是对相关变量的操作。
20
指针是地址的独立存在的变量,指针的值是变量的地址,是地址的编号,为 4字节或 2字节的无符号整数。
指针 p可以遍历存储单元,对存储单元的内容进行访问或数据更新。
间接变量 *p的值是相关变量的值,对间接变量 *p的操作是对相关变量的操作。
可以说指针与引用都是变量的别名:
指针是变量的移动的间接别名,引用是变量的固定的直接别名。指针是变动的,可以指向变量、数组、对象,对于数据的流通提供了最高的灵活性。引用是凝固的,是指针与变量的中介,引用保留了指针对数据的快速流通与变量名的简洁,是指针与变量典型的混合物。
21