设为首页 加入收藏

TOP

C语言进阶指南(3)丨显式内联、矢量扩展、C的逸闻轶事(一)
2019-04-07 16:07:48 】 浏览:304
Tags:语言 进阶 指南 内联 矢量 扩展 逸闻轶事

五、显式内联

(想让)函数代码被直接集成到调用函数中,而非产生独立的函数目标和单个调用,可显式地使用 inline 限定符来指示编译器这么做。根据section 6.7.4 of C standardinline 限定符仅建议编译器使得”调用要尽可能快”,并且“此建议是否有效由具体实现定义”

要用内联函数优点的最简单方法是把函数定义为 static ,然后将定义放入头文件。

/* middle.h */

static inline int middle(int a, int b){

    return (b-a)/2;

}

独立的函数对象仍然可能被导出,但在翻译单元的外部它是不可见的。这种头文件被包含在多个翻译单元中,编译器可能为每个单元发射函数的多份拷贝。因此,有可能两个变量指向相同的函数名,指针的值可能不相等。

另一种方法是,既提供外部可连接的版本,也提供内联版本,两个版本功能相同,让编译器决定使用哪个。这实际上是内嵌限定符的定义:

If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition.

在一个翻译单元中,若某个函数在所有的文件范围内都包含不带extern的内联函数限定符,则此翻译单元中此函数定义是内联定义。内联定义不为函数提供外部的定义,也不禁止其他翻译单元的外部定义。内联定义为外部定义提供一个可选项,在同一翻译单元内翻译器可用它实现对函数的任意调用。调用函数时,使用内联定义或外联定义是不确定的。

(译者注:即gcc中的 extern inline,优先使用内联版本,允许外部版本的存在)

对于函数的两个版本,我们可以把下面的定义放在头文件中:

/* middle.h */

inline int middle(int a, int b){

    return (b-a)/2;

}

然后在具体的源文件中,用extern限定符发射翻译单元中外部可链接的版本:

#include "middle.h"

extern int middle(int a, int b);

GCC编译器的实现不同于上述译码方式。若函数由 inline 声明,GCC总是发射外部可链接的目标代码,并且程序中只存在一个这样的定义。若函数被声明为export inline的,GCC将永不为此函数发射外部可链接的目标代码。自GCC 4.3版本起,可使用-STD= c99的选项使能为内联定义使能C99规则。若C99的规则被启用,则定义GNUC_STDC_INLINE。之前描述的 static使用方法不受GCC对内联函数解释的影响。如果你需要同时使用内联和外部可链接功能的函数,可考虑以下解决方案:

/* global.h */

#ifndef INLINE

# if __GNUC__ && !__GNUC_STDC_INLINE__

#  define INLINE extern inline

# else

#  define INLINE inline

# endif

#endif

头文件中有函数定义:

/* middle.h  */

#include "global.h"

INLINE int middle(int a, int b) {

  return (b-a)/2;

}

在某个具体实现的源文件中:

#define INLINE

#include "middle.h

若要对函数强制执行内联,GCC和Clang编译器都可用always_inline 属性达成此目的。下面的例子中,独立的函数对象从未被发射。

/* cdefs.h */

# define __always_inline   inline __attribute__((always_inline))

 

/* middle.h */

#include <cdefs.h>

static __always_inline int middle(int a, int b) {

  return (b-a)/2;

}

一旦编译器内联失败,编译将因错误而终止。例如Linux kernel就使用这种方法。可在 cdefs.h 中上述代码中使用的__always_inline。

 

六、矢量扩展

许多微处理器(特别是x86架构的)提供单指令多数据(SIMD)指令集来使能矢量操作。例如下面的代码:

#include <stdint.h>

#include <string.h>

#define SIZE 8

int16_t a[SIZE], b[SIZE];

 

void addtwo(){

    int16_t i = 0;

 

    while (i < SIZE) {

        a[i] = b[i] + 2;

        i++;

    }

}

 

int main(){

    addtwo();

    return a[0];

}

addtwo 中的循环迭代 8 次,每次往数组 b 上加 2,数组 b 每个元素是 16 位的有符号整型。函数 addtwo 将被编译成下面的汇编代码:

$ gcc -O2 auto.c -S -o auto_no.asm

addtwo:

.LFB22:

        .cfi_startproc

        movl    $0, %eax

.L2:

        movzwl  b(%rax), %edx

        addl    $2, %edx

        movw    %dx, a(%rax)

        addq    $2, %rax

        cmpq    $16, %rax

        jne     .L2

        rep

        ret

        .cfi_endproc

起初,0 写入到 eax 寄存器。标签 L2 标着循环的开始。b 的首个元素由movzwl指令被装入的32位寄存器 edx 前

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇纪念第一次写博客 下一篇C语言进阶指南(2)丨数组和指针..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目