9

我对inlineC99感到困惑。

这是我想要的:

  1. 我希望我的函数在任何地方都被内联,而不仅仅是一个翻译单元(或一个编译单元,一个.c文件)。
  2. 我希望函数的地址一致。如果我将函数的地址保存在函数指针中,我希望函数可以从指针调用,并且我不希望在不同的翻译单元中重复相同的函数(基本上,我的意思是 no static inline)。

C++inline正是这样做的。

但是(如果我错了,请纠正我)在 C99 中没有办法得到这种行为。

我可以使用static inline,但它会导致重复(不同翻译单元中相同函数的地址不一样)。我不想要这种重复。

所以,这是我的问题:

  1. inlineC99背后的想法是什么?
  2. 与 C++ 的方法相比,这种设计有什么好处?

参考:

  1. 这是一个高度评价 C99 的链接inline,但我不明白为什么。这种“仅在一个编译单元中”的限制真的那么好吗?
    http://gustedt.wordpress.com/2010/11/29/myth-and-reality-about-inline-in-c99/
  2. 这是 C99 的基本原理inline。我读过,但我不明白。
    没有“静态”或“外部”的“内联”在 C99 中有用吗?
  3. 一篇不错的帖子,提供了使用inline函数的策略。
    http://www.greenend.org.uk/rjk/tech/inline.html

答案摘要

如何inline在 C99 中获得 C++ 行为(是的,我们可以)

head.h

#ifndef __HEAD_H__
#define __HEAD_H__

inline int my_max(int x, int y) {
    return (x>y) ? (x) : (y);
}

void call_and_print_addr();

#endif

src.c

#include "head.h"
#include <stdio.h>

// This is necessary! And it should occurs and only occurs in one [.c] file
extern inline int my_max(int x, int y); 

void call_and_print_addr() {
    printf("%d %u\n", my_max(10, 100), (unsigned int)my_max);
}

main.c

#include <stdio.h>
#include "head.h"

int main() {
    printf("%d %u\n", my_max(10, 100), (unsigned int)my_max);
    call_and_print_addr();

    return 0;
}

编译它:gcc -O3 main.c src.c -std=c99
检查程序集:gcc -O3 -S main.c src.c -std=c99,你会发现它在和my_max中都内联。 call_and_print_addr()main()

实际上,这与参考 1 和参考 3 给出的指令完全相同。我怎么了?

我使用了一个太旧版本的 GCC (3.4.5) 进行实验,它给了我“多重定义my_max”的错误信息,这才是我如此困惑的真正原因。耻辱。

C99 和 C++ 的区别inline

实际上,您可以通过以下方式编译上面的示例g++g++ main.c src.c

extern inline int my_max(int x, int y);

在 C++ 中是多余的,但在 C99 中是必需的。

那么它在 C99 中的作用是什么?

同样,使用gcc -O3 -S main.c src.c -std=c99,您会在以下位置找到类似的内容src.s

_my_max:
    movl    4(%esp), %eax
    movl    8(%esp), %edx
    cmpl    %eax, %edx
    cmovge  %edx, %eax
    ret
    .section .rdata,"dr"

如果您将其剪切extern inline int my_max(int x, int y);并粘贴到main.c中,您将在 中找到这些汇编代码main.s

所以,extern inline你告诉编译器真正的函数my_max(),你可以通过它的地址调用它,将在哪里定义和编译。

现在回头看 C++,我们无法指定它。我们永远不知道my_max()会在哪里,这就是@Potatoswatter 的“模糊联系”。

正如@Adriano 所说,大多数时候,我们并不关心这个细节,但 C99 确实消除了歧义。

4

2 回答 2

6

要获得类似 C++ 的行为,您需要为每个具有潜在内联调用的 TU 提供一个inline定义,并为一个 TU 提供一个外部可见的定义。这正是 C 标准的相关部分(函数说明符)中的示例 1 所说明的内容。inline(在该示例中,外部可见性通过在之后声明函数来追溯应用于定义extern:此声明可以在.c文件中定义之后在.h文件中完成,这颠覆了通常的用法。)

如果内联可以在任何地方完成,你就不需要这个extern函数。但是,在递归和引用函数地址等上下文中使用非内联调用。从某种意义上说,您可以通过省略部分来获得“始终内联”的语义,extern但是对于任何简单的函数调用,这可能会任意失败,因为标准不要求内联调用只是因为别无选择。(这是链接问题的主题。)

C++ 用“模糊链接”的实现概念来处理这个;这在标准中没有指定,但在编译器内部它是非常真实且棘手的。C 编译器应该比 C++ 更容易编写;我相信这解释了语言之间的差异。

于 2014-04-24T15:31:33.433 回答
3

我希望我的函数在任何地方都能内联,而不仅仅是一个翻译单元(或一个编译单元,一个 [.c] 文件)。

使用 inline,您礼貌地要求您的编译器内联您的函数(如果它有时间和心情)。它与一个编译单元无关,充其量它甚至可以在每个调用站点中内联,并且它不会在任何地方有一个主体(并且它的代码将在任何地方重复)。这是内联的目的,速度有利于大小。

我希望函数的地址一致。如果我将函数的地址保存在函数指针中,我希望函数可以从指针调用,并且我不希望在不同的翻译单元中重复相同的函数。(基本上,我的意思是没有“静态内联”)

你又不能。如果函数是内联的,则没有任何函数指针指向它。当然,编译器需要一个编译单元来保存函数(因为,是的,你可能需要一个函数指针,或者有时它可能决定不在特定的调用站点内联该函数)。

从你的描述看来,static inline还不错。IMO 不是,每个编译单元中的函数体(使用时,见上段)会导致代码重复(以及函数指针比较问题,因为每个编译单元都有自己的函数版本)。正是在这里,C99 做了一件非常好的事情:声明了一个放置函数体的地方(当需要时)。编译器不会为你做这件事(如果你曾经关心它的话),并且没有任何东西留给实现者。

C99 中内联的想法是什么?

选择一个好东西(内联函数),但要消除歧义(每个 C++ 编译器都做了自己的工作,即函数体必须留在哪里)。

与 C++ 的方法相比,这种设计有什么好处?

老实说,我看不到这么大的问题(即使您链接的文章对这个好处也很模糊)。在现代编译器中,您不会看到任何问题,并且您永远不会关心它。为什么C做的很好?IMO 因为它消除了歧义,即使 - 坦率地说 - 我更希望我的编译器在我不关心它的时候为我做这件事(我想是 99.999%)。

也就是说,但我可能错了,C 和 C++ 有不同的目标。如果您使用的是 C(不是没有类和很少 C++ 功能的 C++),那么您可能想要解决这类细节,因为它们在您的上下文中很重要,因此 C 和 C++ 不得不对此有所分歧。没有更好的设计:只是针对不同的受众做出不同的决定。

于 2014-04-24T15:48:52.143 回答