网站建设中,中国大陆地区请使用VPN访问,欢迎提建议,关注LSKR Mastodon

详细解释C语言中的内联函数-C语言高级用法

本文详细解释了C语言重点内联函数的常规用法以及高级用法,同时示范了内联函数的很多错误用法。
Estimated read time: 1 min
详细解释C语言中的内联函数-C语言高级用法

内联函数的作用

在 C 语言中,inline 关键字用于建议编译器将函数 内联展开,也就是把函数的代码直接插入到调用处,从而 避免函数调用的开销。但是否真的内联,由编译器决定。

在 C 中,inline 函数通常具有两个目的:

请求编译器内联展开该函数(虽然不是强制,编译器可能忽略)。

错误示范

 myheader.h:
 inline void foo(); // 仅声明 这里是 仅声明 了一个 inline 函数 foo,但并没有提供定义(即函数体)。
 foo.c:
 inline void foo() {
     // ...
 }
 //这是对 foo 的定义(包含函数体),同样使用了 inline 关键字。

当前写法的问题:

如果你只在一个 .c 文件中定义 inline 函数(且未加 extern),编译器可能不会为它生成链接可见的符号,导致在其他文件中调用 foo() 时找不到定义。

  • myheader.h 中的 inline void foo(); 没有定义函数体;

  • foo.c 中定义了 inline 函数;

但这会导致其他源文件无法使用 foo(),因为 inline 函数不会自动生成链接符号,应将定义放到头文件中改用非 inline 的方式

static inline

为什么加了 static 就没问题?

 static inline void foo() {
     // ...
 }

加了 static 后,foo 的作用域就局限在当前 .c 文件里:

  • 每个 .c 文件有自己的一份 foo

  • 这些函数是“私有”的,不参与链接

  • 所以多个 .c 文件用同一个头文件不会冲突

分解解释:

inline 的作用是:

  • 建议编译器将函数体在调用处“展开”成内联代码,省去函数调用开销,更高效。

  • 适合:小函数、频繁调用的场景。

static 的作用是:

  • 把函数的作用域限制在当前 .c 文件。

  • 如果不加 static,你把一个函数定义写在头文件中,多个 .c 文件 #include 之后会导致 “多重定义”错误(link error)。

  • 加了 static 后,每个 .c 文件都有一份自己的副本,彼此互不干扰。

什么时候该用 static inline

  • 小工具函数(如 max(), swap(), list_add() 等)

  • 多个 .c 文件都需要用

  • 函数体很小,性能敏感

  • 不希望暴露成链接符号

不用!

  • 函数体太大(会膨胀代码体积)

  • 你希望别的文件能访问它(那就不要 static

问题分析:

 // myheader.h
 inline void foo(); // 仅声明
 
 // foo.c
 inline void foo() {
     // ...
 }

但这要求你:

  • 在某个 .c 文件里要有 唯一定义

  • 所有用的地方都只能用声明,不能定义

  • 不然仍然会多重定义

你这个例子中,如果使用的是 C 编译器(如 gcc foo.c),那会出现以下问题:

  • myheader.h 中,你 只声明了一个 inline 函数,但是没有提供定义。

  • 然后你在 foo.c 中定义了它。

inline 函数若想在其他文件中使用,必须在头文件中提供定义。因为编译器在编译其他源文件时,需要看到函数体,才能考虑内联或生成调用。

为啥要写在头文件中?

因为内联函数是编译器在编译阶段直接“插入”调用点的代码。每个用到它的编译单元(.c 文件)都需要看到这个函数的定义,而不仅仅是声明。

如果你只写了声明:

 inline void foo();  // 👈 只有这个,别的文件编译不了 foo

那其它文件编译器就不知道 foo() 的具体内容,无法展开成内联代码,也无法链接(除非你链接了一个带外部定义的 foo,但那就和 inline 本意矛盾了)。

内联函数不在头文件中的用法

那只写 inline 就不能用吗?

可以 —— 但你要写成“外部内联声明+单一定义”的格式(太复杂,不推荐初学者这么做):

 // myheader.h
 extern void foo(); // 普通声明,不要用 inline
 
 // foo.c
 inline void foo() {
     // ...
 }
 extern void foo(); // 告诉编译器你会用到这个函数,强制生成符号

但这个方式较为复杂,不推荐,除非你特别清楚目的。

所以实际开发中,为了方便和安全,大家基本都用 static inline 放头文件

写法是否安全放在头文件会不会冲突推荐程度
inline void foo()❌ 不安全(可能多重定义)⚠️ 高❌ 不推荐
static inline void foo()✅ 安全,每个 .c 一份✅ 不会✅ 强烈推荐
extern inline + 单一定义✅ 安全但麻烦✅ 不会🔧 高级用法

总结

内联函数用于小函数,提高性能,通常用法有两种,

一:常见用法,直接写成static inline在头文件中,

二:写成外部内联声明+单一定义,这是放在源文件中的用法,其他用法由于语法等问题,不成立。(麻烦所以不建议使用),所以内联有且只有一种用法 static inline

补充C++语言内联机制大不同

inline 的函数,即使在多个 .cpp 文件中包含,也不会重复定义,因为 inline 函数具有“多重定义容忍性”。(C++属性,在C中没有这个机制)

 // math_utils.h
 inline int square(int x) {
 return x * x;
 }

这样的定义用法在C语言是不推荐的。这是在C++会出现的用法,原因就是C++具有“多重定义容忍”性特性。

إرسال تعليق

Cookie Consent
我们使用 Cookie 来了解您如何使用我们的网站并提升您的体验。这包括个性化内容和广告。
Oops!
It seems there is something wrong with your internet connection. Please connect to the internet and start browsing again.
AdBlock Detected!
We have detected that you are using adblocking plugin in your browser.
The revenue we earn by the advertisements is used to manage this website, we request you to whitelist our website in your adblocking plugin.
Site is Blocked
Sorry! This site is not available in your country.