static与inline

笔者在阅读一些开源代码时,发现很多头文件都会使用static inline关键字,以linux内核的list.h函数为例:

/**
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}

/**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

这不禁让笔者产生了好奇,static笔者一般是用来修饰变量或函数限定作用域实现封装功能,inline一般是为了效率考虑,建议编译器选择展开函数,而不是直接ldr调用,那么static inline是什么用法呢?

先放结论:static是为了防止重定义错误,因为该函数定义在头文件,编译时会被替换到引入的源文件中,不使用static关键字,那么会出现多个函数重定义问题。至于inline,就是为了展开该函数。

现在我们知道这与编译有关,但是在讲解编译的基本知识前,读者不妨做个小实验验证一下结论:

headfun.h

//
// Created by el on 2024/9/8.
//

#ifndef TRY_CONFIG_H
#define TRY_CONFIG_H

inline int config1()
{
    int a = 1;
    return a;
}


#endif //TRY_CONFIG_H

one.c

//
// Created by el on 2024/9/8.
//

#include "one.h"
#include "config.h"

int  firsttry()
{
    int a =config1();
    return a;
}

one.h

//
// Created by el on 2024/9/8.
//

#ifndef TRY_ONE_H
#define TRY_ONE_H
int  firsttry();


#endif //TRY_ONE_H

two.c

//
// Created by el on 2024/9/8.
//

#include "two.h"
#include "config.h"

int  twotry()
{
    int a =config1();
    return a;
}

two.h

//
// Created by el on 2024/9/8.
//

#ifndef TRY_TWO_H
#define TRY_TWO_H


int twotry();

#endif //TRY_TWO_H

程序运行,就会报重定义错误,对headfun.h文件进行修改:

headfun.h

//
// Created by el on 2024/9/8.
//

#ifndef TRY_CONFIG_H
#define TRY_CONFIG_H

static inline int config1()
{
    int a = 1;
    return a;
}


#endif //TRY_CONFIG_H

此时报错就会消失。

c程序在被编译时,会先进行预处理,把源文件中的头文件声明换成对应头文件内容。这就是static存在的意义-限制函数作用域,防止重定义。

预处理器详细流程如下:

C/C++预处理器(Preprocessor)是C/C++编译过程的第一步。它主要负责处理源代码文件中的预处理指令。预处理指令通常以#开始,例如#include#define#if等。预处理器的工作流程可以分为以下几个主要步骤:

  1. 宏定义替换:预处理器会查找所有的宏定义,然后替换程序中的宏。
  2. 头文件处理:对于#include指令,预处理器会将指定的文件内容插入到该指令所在的位置。
  3. 条件编译:预处理器会根据#if#ifdef#ifndef#else#elif#endif指令,决定是否编译代码的特定部分。
  4. 移除注释:预处理器移除源代码中的所有注释。
  5. 添加行号和文件名信息:为了在编译过程中产生准确的错误和警告信息,预处理器会添加行号和文件名信息。
  6. 生成预处理后的源代码:完成上述步骤后,预处理器会生成一个预处理后的源代码文件,这个文件将被编译器用于后续的编译过程。

至于static发挥作用,如果要详细的说,是在生成符号表时,编译器会为每一个作用域建立单独的符号来实现作用域。每一个带有声明的程序块都有自己的符号表。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值