(一〇二)内联函数
内联函数和普通函数有所区别。
普通函数需要①函数定义②函数原型③函数调用,具体调用形式是,先调用时,
储存当前函数的内存地址(假设为地址 A),然后调用函数,跳到被调用函数的
内存地址(地址 B),然后在地址 B 处依次执行,等执行完被调用的函数(或许
还有返回值),再返回之前储存的地址 A 处。
内联函数,可以省略②函数原型(也可以不省略),但需要在函数定义和函数原
型的函数头之前加上关键字 inline(如果省略函数原型的话,则一般将函数定义
放在函数原型的地方),
他的机制,是把函数代码,放在函数调用时的内存地址处,这样的话,无需像普
通函数那样,从地址 A 处跳到地址 B 处,调用完再跳回来,而是地址 A 处之后
的内存位置,就是原本是地址 B 处函数的代码内容。就像直接将函数命令放在调
用处一样。
例如:
int main()
{
int a=x(1);
return 0;
}
int x(int m)
{
return m+3;
}
在普通函数时,int a 的内存地址是地址 A,而 x 函数被储存在地址 B 处(注意,
B 和 A 并不一定相邻,或者说,几乎不会相邻)。于是程序访问内存时的顺序:
地址 A——地址 B——地址 A 这样。
而在内联函数时,在 int x(int m)之前加上 inline,在 main 函数中,有点类似于:
int a={ 1 + 3 };
这样的形式。
由于内联函数是直接放在 main 函数中(而不是调用的时候才占用内存),因此,
使用内联函数更耗费内存。但相对而言,使用内联函数的优点则是速度更快(因
为不用在内存之中跳来跳去)。
但,由于在内存地址中跳来跳去速度其实很快,所以节省的时间的绝对值,并不
大,除非该函数经常被调用(每次节约 0.01 秒,100 次就是 1 秒了)。
内联函数的优点:速度更快——适合执行代码时间很短的函数;
内联函数的缺点:占用内存更多——不适合较长的函数。
编译器不允许使用内联函数的两种情况:
①递归函数——内联函数不允许自己调用自己;
②函数过大。
内联函数的使用方式:
①在函数原型和函数定义的函数头之前加上关键字 inline 即可;
②可省略函数原型,若省略,则将函数定义放置于函数原型处较好;
③其他同普通函数。
如代码:
#include
inline int chengfang(int a)
//函数定义前置于函数原型处,并可省略函数原型
{
}
return a*a;
int main()
{
}
using namespace std;
int b = chengfang(2); //像普通函数那样调用内联函数
int c = chengfang(5);
cout << "b = " << b << ". c = " << c << endl;
cin.get();
return 0;
输出:
b = 4. c = 25
与内联函数对应的宏:
在 C 语言中,使用预处理器语句#define 来提供宏——内联代码的原始实现。
例如宏:#define SQUAR(X) X*X;
宏的特点是文本替换,而不像内联函数那样是函数调用——也就意味着,没有括
号的情况下,结果和内联函数的结果不同。
如 define 的代码:
#include
#define square(x) x*x;
int main()
{
using namespace std;
int a[5];
a[0] = square(1); //实质是 1*1=1
a[1] = square(2); //实质是 2*2=4
a[2] = square(2 * 2); //实质是 2*2 * 2*2=16
a[3] = square(2 + 2); //实质是 2 + 2*2 + 2=8
a[4] = square(2 + 0); //实质是 2 + 0*2 + 0=2
for (int i = 0;i < 5;i++)
cout << "a[" << i << "] = " << a[i] << endl;
system("pause");
return 0;
}
输出:
a[0] = 1
a[1] = 4
a[2] = 16
a[3] = 8
a[4] = 2
请按任意键继续. . .
使用内联函数的代码:
#include
inline int square(int x) { return x*x; }
int main()
{
using namespace std;
int a[5];
a[0] = square(1); //实质是 1*1
a[1] = square(2); //实质是 2*2=4
a[2] = square(2 * 2); //实质是(2*2) * (2*2)=16
a[3] = square(2 + 2); //实质是(2+2) * (2+2)=4*4=16
a[4] = square(2 + 0); //实质是(2+0) * (2+0)=2*2=4
for (int i = 0;i < 5;i++)
cout << "a[" << i << "] = " << a[i] << endl;
system("pause");
return 0;
}
输出:
a[0] = 1
a[1] = 4
a[2] = 16
a[3] = 16
a[4] = 4
请按任意键继续. . .
由上述两个例子可以看出,#define 宏的效果,和 inline 内联函数的机制完全不同。
#define 是文本替换,将括号里的内容直接赋值到表达式之中——而不是括号里
表达式的结果。
inline 是像函数调用那样,将表达式的值得出值的结果后,再放入函数内部的表
达式之中。
特别是比如说:#define MM(m) m*m*m; 这样的宏,
当遇见调用 int a=1; int b=MM(a++);时,
在代码中,b=1*2*3=6,
而不是内联函数的 b=1*1*1(因为 a++是先执行完这行代码之后,a=a+1);
(一〇三)引用变量
格式:
int a = 1;
int &b = a;
//b 作为 a 的别名,是一个 a 变量的引用,需要在声明时进行初始化
效果:
①b 是 a 的引用变量,b 的值和地址,和 a 是相同的,且不会改变;
②在声明引用变量 b 时,需要同时对变量 b 进行初始化,即不能先声明,然后再
初始化;原因是引用变量 b 需要根据他被引用的变量,而拥有地址,而不是有自
己独立的内存地址;
③引用变量的地址,由初始化时的变量决定,而不是由最近一次赋值而决定。例
如 b 是 a 变量的引用,然后又有变量 c 将值赋给 b,那么 b 的地址依然是 a,而
不是 c;
④引用变量其在声明的时候,变量名前的“&”是类型标识符的一部分,而不是
地址运算符。
在之后在引用变量名前加上“&”,才是地址运算符;
⑤引用变量的类型,需要和被引用的变量的类型相同。即 a 是 int 类型,那么引
用变量 b 也应该是 int 类型;
⑥引用变量和指针不同。
指针是指向某个内存地址,而引用变量是变量的别名。
指针有其储存自己的内存地址(即指针指向的地址,是储存指针的内存地址的
值),而引用变量的地址,就是被引用的变量的内存地址。
⑦可以将引用变量理解为变量的另外一个名字,例如上面代码中的 a 和 b,在很
多地方的使用方式是相同的。只不过引用变量还有自己独特的使用范围。
如代码:
#include
int main()
{
using namespace std;
int a = 1;
int &b = a;
//b 作为 a 的别名,是一个 a 变量的引用,需要在声明时进行初始化
cout << a << "," << b << endl; //输出 a、b 的值
cout << &a << "," << &b << endl;
//输出 a、b 的地址
cout << endl;
int c = 2;
b = c;
//将 c 的值赋给 b,注意,这不是让 b 成为 c 变量的别名。
cout << c << "," << &c << endl;
cout << a << "," << b << endl;
cout << &a << "," << &b << endl;
system("pause");
return 0;
}
输出:
1,1
003EFEC0,003EFEC0
2,003EFEA8
2,2
003EFEC0,003EFEC0
请按任意键继续. . .
总结:
①从上面的代码中可以看出,b 和 a 的地址、值是相同的;
②在中间 b=c,然而 b 并没有和 c 的地址保持相同,而是和 a 的地址依然保持相
同,但 b 的值、以及 a 的值,和 c 的值相同,相当于将 c 的值,赋给了 a 和 b;
③当引用变量或者被引用的变量的值改变时,另外一个值也一样被改变(因为实
质上是一个值有两个变量名);
④可以一个变量有多个引用变量。如在之前代码的基础上,加上 int &d=a 或 int
&d=b; 然后 d 的地址和值,也将和 a、b 保持相同;
⑤必须声明引用变量的时候进行初始化。
引用变量作为函数参数时:
引用变量作为函数参数时,可以像指针那样直接修改变量的值,但是具体方式不
同。
使用指针修改实参时,传递的实参应为变量的地址,在函数内部,应该如同使用
指针那样,通过地址进行操控值;
例如代码:
//使用指针修改变量的值
#include
int chenger(int*);
//传递的参数应为指针
int main()
{
}
using namespace std;
int a = 1;
int b = chenger(&a);
//调用函数时,传递的是变量的地址
cout << a << ", " << b << endl;
system("pause");
return 0;
int chenger(int*m)
{
*m *= 2; //用指针的方式,修改指针指向的值
return *m;
//返回应为指针加上接触运算符*
}
使用引用修改实参时,传递的值是变量自己,参数是引用变量的标识符“&”,
在函数内部,像使用变量那样进行修改。
如代码:
//使用引用修改变量的值
#include
int chenger(int&);
//传递的参数为变量的别名
int main()
{
}
using namespace std;
int a = 1;
int b = chenger(a);
//调用函数时,传递的是变量本身,而非加上地址运算符的&a
cout << a << ", " << b << endl;
system("pause");
return 0;
int chenger(int&m)
{
}
m *= 2; //用修改变量的方式修改变量
return m;
//返回值为变量名或者相关的表达式
二者的效果是相同的。输出的内容都是“2,2”
引用变量和指针作为函数的参数时的差别:
①参数的形式不同:引用变量是运算符&,指针是*;
②传递的内容不同:引用变量传递的是值,指针传递的是地址;
③使用方式不同:引用变量像使用变量一样使用,指针需要按指针的方式进行使
用;
④方便程度不同:引用变量在某些时候,由于不用加解除运算符“*”,在操纵
值的时候更简单;
⑤都能直接对参数进行修改,而直接按值传递(不使用引用变量或者指针时),
修改的值是实参的副本(形参)。
引用变量和常量作为参数时:
①函数原型和函数头中的参数不同;
②在调用函数时,引用变量的参数通常不能是表达式(而是变量名)(使用 const
作为参数的限定除外),而常量作为参数时,可以是表达式;
如 int A(int&); 中,
参数可以这么写:A(a)——a 为变量名,
但不能这么这么写:A(a+1) 或 A(a+b)——b 也为变量名
③在使用引用变量作为参数时,假如需要不能修改引用变量的值,那么应该使用
常量引用,即在函数头之前加 const 进行限定(如果被修改了,编译器会提示出
错);
④对于基本类型的传递,假如需要限定不能被修改,那么不如使用按值传递的方
式(即让函数在调用时,创建变量的副本);
⑤对于数据比较大(如结构和类)时,引用参数将会很有用。
临时变量:
之前提到,使用引用变量的函数,通常在调用的时候,参数不能是表达式。
但是在使用 const 进行限定时,则可以在调用的时候使用表达式,如:
int chenger(const int&);
int b = chenger(a+1);
在这两行代码中,第一行是函数原型,使用引用变量,但同时使用了关键字 const。
于是,在调用时,就可以使用表达式 a+1。
但同样,因为关键字 const 的限定,因此变量 a 在函数 chenger 中,并没有像普
通引用变量那样被修改。
如果实参与引用参数不匹配,C++将生成临时变量。
当前,仅当参数为 const 引用时,C++才允许这么做(如上)。
在引用参数是 const 的情况下,编译器将在两种情况下生成临时变量(早期的 C++