C语言里面的内联函数(inline)与宏定义(#define)探讨

先简明扼要,说下关键:

1、内联函数在可读性方面与函数是相同的,而在编译时是将函数直接嵌入调用程序的主体,省去了调用/返回指令,这样在运行时速度更快。

2、内联函数可以调试,而宏定义是不可以调试的。

内联函数与宏本质上是两个不同的概念如果程序编写者对于既要求快速,又要求可读的情况下,则应该将函数冠以inline。下面详细介绍一下探讨一下内联函数与宏定义。

一、内联函数是什么?

内联函数是代码被插入到调用者代码处的函数。如同 #define 宏(但并不等同,原因见下文),内联函数通过避免被调用的开销来提高执行效率,尤其是它能够通过调用(“过程化集成”)被编译器优化。

二、内联函数是如何在安全和速度上取得折衷?

在 C 中,你可以通过在结构中设置一个 void* 来得到“封装的结构”,在这种情况下,指向实际数据的 void* 指针对于结构的用户来说是未知的。因此结构的用户不知道如何解释void*指针所指内容,但是存取函数可以将 void* 转换成适当的隐含类型。这样给出了封装的一种形式。

不幸的是这样做丧失了类型安全,并且也将繁琐的对结构中的每个域的访问强加于函数调用。(如果你允许直接存取结构的域,那么对任何能直接存取的人来说,了解如何解释 void* 指针所指内容就是必要的了;这样将使改变底层数据结构变的困难)。

虽然函数调用开销是很小的,但它会被累积。C++类允许函数调用以内联展开。这样让你在得到封装的安全性时,同时得到直接存取的速度。此外,内联函数的参数类型由编译器检查,这是对 C 的 #define 宏的一个改进。

三、为什么我应该用内联函数?而不是原来清晰的 #define宏?

因为#define宏定义函数是在四处是有害的:

和 #define 宏不同的是,内联函数总是对参数只精确地进行一次求值,从而避免了那声名狼藉的宏错误。换句话说,调用内联函数和调用正规函数是等价的,差别仅仅是更快:

// 返回 i 的绝对值的宏

#define unsafe(i) \

( (i) >= 0 ? (i) : -(i) )

// 返回 i 的绝对值的内联函数

inline

int safe(int i)

{

return i >= 0 ? i : -i;

}

int f();

void userCode(int x)

{

int ans;

ans = unsafe(x++);   // 错误!x 被增加两次

ans = unsafe(f());   // 危险!f()被调用两次

ans = safe(x++);     // 正确! x 被增加一次

ans = safe(f());     // 正确! f() 被调用一次

}

和宏不同的,还有内联函数的参数类型被检查,并且被正确地进行必要的转换。

宏定义复杂函数是有害的;非万不得已不要用。

四、如何告诉编译器使非成员函数成为内联函数?

声明内联函数看上去和普通函数非常相似:

void f(int i, char c);

当你定义一个内联函数时,在函数定义前加上 inline 关键字,并且将定义放入头文件:

inline

void f(int i, char c)

{

// ...

}

注意:将函数的定义({...}之间的部分)放在头文件中是强制的,除非该函数仅仅被单个 .cpp 文件使用。尤其是,如果你将内联函数的定义放在 .cpp 文件中并且在其他 .cpp文件中调用它,连接器将给出 “unresolved external” 错误。

五、如何告诉编译器使一个成员函数成为内联函数?

声明内联成员函数看上去和普通函数非常类似:

class Fred {

public:

void f(int i, char c);

};

但是当你定义内联成员函数时,在成员函数定义前加上 inline 关键字,并且将定义放入头文件中:

inline

void Fred::f(int i, char c)

{

// ...

}

通常将函数的定义({...}之间的部分)放在头文件中是强制的。如果你将内联函数的定义放在 .cpp 文件中并且在其他 .cpp 文件中调用它,连接器将给出“unresolved external”错误。

六、有其它方法告诉编译器使成员函数成为内联吗?

有:在类体内定义成员函数:

class Fred {

public:

void f(int i, char c)

{

// ...

}

};

尽管这对于写类的人来说很容易,但由于它将类是“什么”(what)和类“如何”(how)工作混在一起.

小结

总之,在嵌入式C(或C++)编程里面,懂得使用内联函数(inline)与宏定义(#define),并使用好它们,对我们是大有裨益的。(注:本文部分内容来源于网络整理,上述探讨属于个人意见,仅供参考。错误之处也是难免!)

胡恩伟

于重庆大学A区一舍

2011年2月19日

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

c语言中void和define,C语言里面的内联函数(inline)与宏定义(#define)探讨相关推荐

  1. 【嵌入式】C语言高级编程-内联函数(10)

    00. 目录 文章目录 00. 目录 01. 属性声明 02. 内联函数概述 03. 内联函数与宏 04. 编译器对内联函数的处理 05. static修饰内联函数 06. 附录 01. 属性声明 a ...

  2. c语言什么是内联函数,C语言中内联函数inline的使用方法

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 来源一:比特网 来源二:东方锐智 在C++中,为了解决一些频繁调用的小涵数大量消耗栈空间或者是叫栈内存的问题,特别的引入了inline修饰符,表示为内联涵 ...

  3. c语言中ndigit用法,求C语言中头文件及函数的含意的总分类

    ALLOC.H 说明内存管理函数(分配.释放等). ASSERT.H 定义 assert调试宏. BIOS.H 说明调用IBM-PC ROM BIOS子程序的各个函数. CONIO. H 说明调用DO ...

  4. c语言inline不起作用,C语言inline内联函数学习小结

    //inline(内联)函数:将普通的函数定义为inline函数,可以避免普通函数入栈出栈的开销,它是将函数内的代码直接粘贴到调用处.除此之外,它和普通函数无异. //要成为inline函数必须具备以 ...

  5. C语言中内联函数的作用 inline

    C语言中内联函数的作用 inline C语言中内联函数到底有什么作用? 试想一下,每当我们在假设就在主函数中调用另外一个函数的时候,那么这个函数就要入栈或者出栈,比如说下面的一个例子: 点击(此处)折 ...

  6. c语言内联函数的声明,C语言内联函数

    一般来说,调用一个函数流程为:当前调用命令的地址被保存下来,程序流跳转到所调用的函数并执行该函数,最后跳转回之前所保存的命令地址. 对于需要经常调用的小函数来说,这大大降低了程序运行效率.所以,C99 ...

  7. c语言void结尾,C语言中void*详解及应用

    void在英文中作为名词的解释为"空虚:空间:空隙":而在C语言中,void被翻译为"无类型",相应的void *为"无类型指针".void ...

  8. c语言中void指针,C 语言 void指针

    C 语言 void指针 到目前为止,我们已经研究了分配给指针的地址应该与指针声明中指定的类型相同. 例如,如果我们声明了int指针,则此int指针不能指向float变量或某种其他类型的变量,即它只能指 ...

  9. c语言里void什么作用,C语言中void是什么意思?

    C语言中void是什么意思? C语言中"void"表示为无类型,相应的"void *"为无类型指针,常用在程序编写中对定义函数的参数类型.返回值.函数中指针类型 ...

最新文章

  1. python--numpy pad函数使用
  2. 数据库的几种联结,union,union all ,inner jion ,left jion,right jion ,cross jion
  3. OSGi Bundle之Hello World
  4. 分布式服务常见问题—分布式事务
  5. 硬盘对应计算机主板,电脑中那些硬件是容易损坏的,机械硬盘和主板
  6. Sql自动更新不同IP的数据库数据。(link Server)
  7. C语言 strspn函数实现
  8. 通过可视化来了解你的Spark应用程序
  9. httpcline转发_go http请求转发
  10. Web开发如何实现Tomcat等服务器热部署不用重启
  11. [算法]复杂链表的复制
  12. linux系统丢失用户环境文件夹,Linux 用户环境变量丢失故障及解决
  13. 笨鸟都没有先飞怎么办。。。
  14. 滴滴上线自动驾驶服务;微软宣布将永久关闭实体店;.NET 5.0 Preview 6 发布 | 极客头条...
  15. Spring入门(三)
  16. java adt真机调试_Unity Android 真机调试
  17. 侧信道攻击实验四 AES CPA 攻击
  18. 止步智能手机,网易的移动互联网冷静剂
  19. 高中生都能看懂的卡方检验
  20. SylixOS的来龙去脉

热门文章

  1. KILE无法软件仿真
  2. 在linux下刷B站方法总结
  3. Java SpringMVC毕业项目实战-学生信息管理系统
  4. 程序员被PUA的一天有多可怕......35 岁,真的是职场荣枯线吗?
  5. 谁将泡泡玛特推上千亿市值神坛?
  6. 成功路上并不拥挤 因为坚持的人不多
  7. 收款神器!解读聚合收款码背后的原理
  8. 单链表的简单操作与演示
  9. 【全栈接口测试进阶系列教程】精通api接口测试,接口分类,接口架构,http,webservice,dubbo接口协议,接口流程,接口工具,cookie,session,token接口鉴权原理以及实战
  10. 【题解】曼哈顿模拟赛(洛谷)