在学习内联函数之前,大家可以写一个实现加法的宏ADD(),检测一下自己。
如果对宏等预编译知识不太熟悉的话,可以先看一下这篇文章:预编译——2.2.4
以下是一个正确的ADD宏:
#define ADD(x,y) ((x)+(y))
灵魂三问:
你在 x 和 y 前面加类型了吗?
x 和 y 都加括号了吗?
整体加括号了吗?
如果以上三个问题都对了,那么恭喜你,宏定义表达式学的还不错
一个简简单单的ADD(),就需要注意这么多问题,可以说宏这个知识点用起来着实是鸡肋,晦涩难懂;
同时宏也是不支持调试的,也没有类型安全的检查。
发明c++的大佬无疑也发现了这个问题,于是引出了内联函数的概念:用函数的语法解决宏的事。
可能兄弟们要说了,直接用函数不就好了?
如果是一个几十行的函数,函数本身执行就很耗时,那调用函数、创建栈帧的一两行可以忽略不计;但是如果一个函数本身就一两行,因为调用函数本身而产生的那一部分消耗,就格外突出,而宏或者内联的提前替换就显得格外优秀,提高了效率。
只需要在函数的最前面加一句 inline ,如下:
inline int add(int a, int b)
{
return a + b;
}
以 inline 修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。
inline int add(int a, int b)
{
return a + b;
}
int main()
{
int ret = 0, a = 1, b = 2;
ret = a + b;
ret = add(a, b);
}
为了观察内联函数的调用方式,我们看一下汇编代码:
这是Debug模式下main函数的汇编代码。
可以发现 call add (0EA1465h)这一句其实是在调用函数,但是如果按照开始的定义,既然直接展开了,那就不会有调用函数这一步。
这是因为Debug模式下,为了调试方便,编译器默认不进行优化,也就是不会展开,依然使用一般函数的调用方式。
以下,在VS环境下,打开对 inline 的优化再展示一遍:
设置优化inline :
优化后的汇编代码:
(注:汇编代码的打开方式:F10或Fn+F10进到调试模式–>对任意语句右键–>转到反汇编)

可以看到,优化之后inline修饰过的函数会直接展开。
也就是说,在Debug模式下你写代码、调试代码时,inline 相当于普通函数,可以进行调试;在release模式下,又可直接进行原地展开,提高效率(这里指的是避免了调用函数、创建栈帧等方面的资源消耗)
inline是一种以空间换时间的做法,省去调用函数、建立栈帧的额外开销;
但是如果代码很长(一般是10行左右,具体取决于编译器),或者有递归函数,就不适宜使用递归函数。
inline对于编译器而言只是一个建议,编译器归自动优化,如果定义的函数很长或者递归函数等等,编译器优化时会忽略掉内联。
inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
测试:
F.h
#include <iostream>
using namespace std;
inline void f(int i);
F.cpp
#include "F.h"
void f(int i)
{
cout << i << endl;
}
main.cpp
#include "F.h"
int main()
{
f(10);
return 0;
}
报错:
error LNK2019: 无法解析的外部符号 “void __cdecl f(int)” (?f@@YAXH@Z),函数 _main 中引用了该符号
分析:
首先把.h中的内容替换到.cpp中,替换之后
F.cpp
#include <iostream>
using namespace std;
inline void f(int i);
void f(int i)
{
cout << i << endl;
}
在编译过程中,编译器一看,这里的 f() 是个inline,
inline就是说,“我”在调用的地方已经展开了,那么还需要这个给函数一个地址吗?不需要。
也就说F.cpp生成的符号表中,不会有f()的地址。
然后一链接,main.cpp中的 f(10); 就要去F文件给过来的符号表找 f() 的地址,找不到就会报链接错误。
(注:如果大家对编译链接的知识感兴趣,可以看一下这篇文章:点我)
改正:
那怎么写呢?
不在F.cpp中写这个函数的定义,直接把声明、定义(可以只有定义,更香)都放到 F.h 中去,预编译阶段会直接把内联函数换到main.cpp,相当于在main.cpp头顶上定义了这个内联函数,编译阶段就直接在调用的地方被展开。
F.h
#include <iostream>
using namespace std;
void f(int i)
{
cout << i << endl;
}
main.cpp
#include "F.h"
int main()
{
f(10);
return 0;
}
:
- 优点:
1. 增强代码的复用性。
2. 提高性能。
- 缺点:
1. 不方便宏调试(因为预编译就进行了替换)
2. 导致代码可读性变差,可维护性变差,容易误用。
3. 没有类型安全的检查。
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
我需要一个通过输入字符串进行计算的方法,像这样function="(a/b)*100"a=25b=50function.something>>50有什么方法吗? 最佳答案 您可以使用instance_eval:function="(a/b)*100"a=25.0b=50instance_evalfunction#=>50.0请注意,使用eval本质上是不安全的,尤其是当您使用外部输入时,因为它可能包含注入(inject)的恶意代码。另请注意,a设置为25.0而不是25,因为如果它是整数a/b将导致0(整数)。
我需要从json记录中获取一些值并像下面这样提取curr_json_doc['title']['genre'].map{|s|s['name']}.join(',')但对于某些记录,curr_json_doc['title']['genre']可以为空。所以我想对map和join()使用try函数。我试过如下curr_json_doc['title']['genre'].try(:map,{|s|s['name']}).try(:join,(','))但是没用。 最佳答案 你没有正确传递block。block被传递给参数括号外的方法
在这段Ruby代码中:ModuleMClassC当我尝试运行时出现“'M:Module'的未定义方法'helper'”错误c=M::C.new("world")c.work但直接从另一个类调用M::helper("world")工作正常。类不能调用在定义它们的同一模块中定义的模块函数吗?除了将类移出模块外,还有其他解决方法吗? 最佳答案 为了调用M::helper,你需要将它定义为defself.helper;结束为了进行比较,请查看以下修改后的代码段中的helper和helper2moduleMclassC
也许这听起来很荒谬,但我想知道这对Ruby是否可行?基本上我有一个功能...defadda,bc=a+breturncend我希望能够将“+”或其他运算符(例如“-”)传递给函数,这样它就类似于...defsuma,b,operatorc=aoperatorbreturncend这可能吗? 最佳答案 两种可能性:以方法/算子名作为符号:defsuma,b,operatora.send(operator,b)endsum42,23,:+或者更通用的解决方案:采取一个block:defsuma,byielda,bendsum42,23,
所以我正在研究RubyKoans,而且我遇到了一个我认为是ruby1.9.x特有的问题。deftest_calling_global_methods_without_parenthesesresult=my_global_method2,3assert_equal5,resultend我明白了:james@tristan:~/code/ruby_projects/ruby_koans$rake(in/home/james/code/ruby_projects/ruby_koans)cdkoans/home/james/.rvm/rubies/ruby-1.9.2-p180/bin/ru