网站开发作业总结/头条号权重查询

网站开发作业总结,头条号权重查询,网站是否有管理员权限,用ssh做的网站文章目录 0 前言1 从C语言编译说起2 重复定义错误(ODR violation)和条件编译3 内联函数inline和static inline4 总结 0 前言 最近在研究ARM内核代码时,看到core_cm3.h中有大量的内联函数,为此查阅了很多资料,也和朋友讨…

文章目录

    • 0 前言
    • 1 从C语言编译说起
    • 2 重复定义错误(ODR violation)和条件编译
    • 3 内联函数inline和static inline
    • 4 总结

0 前言

  最近在研究ARM内核代码时,看到core_cm3.h中有大量的内联函数,为此查阅了很多资料,也和朋友讨论了很久,最后对C语言多文件编程有了一点不一样的体会,对此前很多习以为常的东西也知道了这么做的原因。特写此文以作总结。

1 从C语言编译说起

  在使用gcc或者g++编译时,直接传入c文件即可得到执行程序,看似非常简单,但实际有很多步骤,包括:预处理(Preprocess),编译(Compile),汇编(Assemble),链接(Link)四个步骤。其中,所谓预处理,即对带有#的语句进行处理,如#include, #define以及条件编译语句#if, #ifdef, #ifndef等(当然,这一步也会进行删除注释等操作);而编译,即是将c语言编译成汇编语言;汇编,是基于汇编语言生成机器码;链接,则是链接具有函数引用关系的不同c文件。

参考链接

  以上这些过程中有一些注意点:

  • #include实际上就是将这个文件的内容复制过来,所以预处理之后,得到的仍然是c格式的文本文件,但体积会比原来的文件大很多。
  • 既然如此,岂不是理论上可以包含任何文件?是的,include某个c文件其实也是允许的,只要复制过来不会报错就行。
  • 那为什么还要建立同名的c和h文件呢?直接一个c文件不行吗?这其实是考虑到C语言不能重复定义函数和变量的特性,以及库文件加调用接口的这种应用场景。一般都是h文件放声明,c文件放定义。至于重复定义错误的相关介绍,参考后面章节。
  • 为什么需要链接?简单来说,只要写的代码中引用了其他文件中定义的函数,就需要链接。这里需要理解一个专业名词:编译实体,即每一个c文件都是一个编译实体,是最小的编译单位。各个编译实体在上述前三个阶段都是独立的,互不影响,而最后的链接阶段就是将不同编译实体给“拼接”到一块,组成一个完整的执行程序。

2 重复定义错误(ODR violation)和条件编译

  相信使用过ADC模块的都遇到过重复定义的问题,即在h文件定义一个转换值的变量,int ADC_value = 0; 然后在main.c中包含这个h文件之后直接使用变量ADC_value,这样就会报重复定义的错误。
  所谓条件编译,即在h文件中用这么一段代码括起来:

#ifndef __FILE_H_
#define __FILE_H_// 中间是头文件的内容#endif

对于这个东西的作用,网上绝大多数的描述都是防止重复包含。确实,从条件编译的逻辑来看可以实现这个功能,但很多人可能会将这个当作上述描述的重复定义错误的解决办法,这显然是不对的。

  首先需要明确,函数或变量的声明,是可以重复include的,如果h文件中只有声明,那完全可以多次include的,那为什么现在的库文件中h文件中都会有上述的条件编译代码呢?确实是防止重复包含,但最终目的不是避免报错,而是加快编译速度

参考链接

  举个例子,有一个库(func.c, func.h)包含了stdio.h,然后main.c包含了func.h,但是出于编写习惯,main.c中也会包含stdio.h,也就是说,最后在编译main.c时就包含了两次stdio.h文件,如果stdio.h文件中没有条件编译,那么它就会被包含两次,虽然不会报错,但会影响编译的速度,而且这种库数量越多,影响越大。

  那重复定义到底是怎么回事呢?如果在h文件中定义全局变量,那么包含该h文件的c文件也就定义了一个全局变量(因为include是完全复制),编译器在编译该c文件时,这个变量就会被存放在全局/静态区。同理,假如该h文件也被其他c文件包含,那么其他包含该h文件的c文件也会这么干,因为不同c文件在预处理,编译和汇编这三个阶段(生成目标文件阶段)是独立的。到这各个c文件都可以被正常编译,不会报错,但是在最后链接阶段时,编译器就会发现全局/静态区存在相同的变量定义,由此报错。

  总结来说,防止重复包含是在前三个阶段,是同一个编译单元编译时的考虑;而重复定义,是不同编译实体之间在第四个阶段链接过程中的问题。因此,防止重复包含并不能解决重复定义的问题。

  所以,对于全局变量,建议采用的方式就是头文件中只声明(extern int a;),定义放在同名的c文件中,这样即使有不同的编译实体包含了该头文件,也只是包含了声明,没有变量定义,这样在链接阶段就不会出现重复定义的问题。

3 内联函数inline和static inline

  inline这个关键词比较复杂,它在不同C语言版本,不同编译器,c和c++中的含义都不尽相同,所以在使用前一定要了解编译环境。

  所谓内联函数,指调用时没有普通函数调用时的堆栈压入和弹出的步骤,而是将函数展开,直接执行内部的代码。内联函数的好处在于减少了函数出入栈的操作,代码执行效率更高,但同样也有缺点,那就是每调用一次,都需要复制一遍函数的代码,空间成本更高,所以内联函数一般只适用于比较简短的代码。

  另外,inline关键词只能建议该函数内联调用,但最终是否调用仍然取决于编译器,所以就有可能会内联失败。对于这个问题,在c++中,一般编译器会将该函数自动转换成普通函数,且只保留一份定义,然后正常调用,从而保证不会出错。比如,在func.h文件中定义一个内联函数,但由于函数内容太长或者其他原因,内联失败了,那么编译器可能会自动创建一个func.c文件(原来没有,不一定是这个名字),然后在这个文件中生成该内联函数的定义,原func.h文件中的定义就只有声明的作用,从而转换成普通函数。

参考链接

  但是,以上是c++的处理方式,可以保证内联的函数有且仅有一份定义,但这并不适用于C语言。先来看一个vscode中的例子:

test.c

#include "stdio.h"inline void func()
{printf("Hello World!\n");
}int main(void)
{func();return 0;
}

点击编译运行,发现会报错:undefined reference to 'func' ... error: ld returned 1 exit status 但是如果将文件改成cpp,同样的代码就不会报错。这看起来好像是C语言编译器的问题?在这篇博客中,介绍了一种办法,就是在函数前再加上static关键词进行修饰,这也就是后面要提到的static inline联合使用的问题,暂且按下不表。

  为解决这个问题,尝试在编译时开启优化,执行gcc -O1 test.c -o run_c; ./run_c,结果发现竟然正常输出了Hello World!换成O2,O3,也都正常。(默认为O0,不开启优化)

在这里插入图片描述

由此可知,如果有inline函数,必须要考虑编译优化等级的问题。

static inline

  再来看看这个static inline,在介绍之前,首先介绍一下static关键词。

  对于static关键词,在我之前的一篇博客中有详细介绍。简单来说,修饰变量时,表示该变量为静态变量,存放在静态区,比如函数中如果使用了静态变量,那么它在内存中的地址就是固定的,全局变量加不加static修饰其实差别不大;修饰函数时,表示该函数只能被当前文件访问,不能被其他文件访问,常用于库的内部函数,不开放对外接口。

  对于修饰函数的情况,static可以起到隔绝作用域的功能。比如,在两个c文件中定义同名函数,且都用static修饰,如下所示:

在这里插入图片描述

这样是可以正常编译运行的。两个test函数虽然一样,但由于被限制在各自的文件中,所以不会造成冲突。

  那为什么inline还要加上static呢?如前所述,inline关键词只能建议编译器将该函数内联展开,如果成功,那么即使内联函数定义所在的文件被多次包含,也可以正常编译运行,举个例子:

在这里插入图片描述

这个例子中,io.h中定义了一个内联函数,另外有两个c文件包含了该h文件,且调用了该内联函数,可以正常编译运行。

  但考虑到inline可能会失败,而且C语言的编译器在这方面又没有c++那么智能,可以自动实现只保留一份定义,避免ODR Violation。那么就需要加上static,这样每次调用都相当于是内部函数,只在该编译实体下可调用,且允许不同编译实体中存在重复定义,这样就能正常编译运行了。总结来说,static起到的是一个安全保障功能。

参考链接

4 总结

  本文从研究内联函数出发,分析了C语言多文件编程的具体流程,并基于次对内联函数的含义和性质、inline和static两个关键词及其组合等内容进行了详细的介绍,对于阅读ARM内核代码有一定的帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/894684.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

10分钟本地部署Deepseek-R1

10分钟本地部署DeepSeek-R1 什么是DeepSeek-R1快速本地部署DeepSeek-R1Ollama下载Ollama安装检查是否安装成功 安装DeepSeek-R1模型模型使用测试 什么是DeepSeek-R1 DeepSeek-R1是中国的深度求索(DeepSeek)公司开发的智能助手。其具有极佳的语义理解和生…

Office / WPS 公式、Mathtype 公式输入花体字、空心字

注:引文主要看注意事项。 1、Office / WPS 公式中字体转换 花体字 字体选择 “Eulid Math One” 空心字 字体选择 “Eulid Math Two” 2、Mathtype 公式输入花体字、空心字 2.1 直接输入 花体字 在 mathtype 中直接输入 \mathcal{L} L \Large \mathcal{L} L…

【C++】STL——vector底层实现

目录 💕 1.vector三个核心 💕2.begin函数,end函数的实现(简单略讲) 💕3.size函数,capacity函数的实现 (简单略讲) 💕4.reserve函数实现 (细节…

7、怎么定义一个简单的自动化测试框架?

定义一个简单的自动化测试框架可以从需求理解、框架设计、核心模块实现、测试用例编写和集成执行等方面入手,以下为你详细介绍: 1. 明确框架需求和范围 确定测试类型:明确框架要支持的测试类型,如单元测试、接口测试、UI 测试等…

AI取代人类?

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

C语言-----数据结构从门到精通

1.数据结构基本概念 数据结构是计算机中存储、组织数据的方式,旨在提高数据的访问和操作效率。它是实现高效算法和程序设计的基石。 目标:通过思维导图了解数据结构的知识点,并掌握。 1.1逻辑结构 逻辑结构主要四种类型: 集合:结构中的数据元素之…

华为小米vivo向上,苹果荣耀OPPO向下

日前,Counterpoint发布的手机销量月度报告显示,中国智能手机销量在2024年第四季度同比下降3.2%,成为2024年唯一出现同比下滑的季度。而对于各大智能手机品牌来说,他们的市场份额和格局也在悄然发生变化。 华为逆势向上 在2024年第…

每日一博 - 三高系统架构设计:高性能、高并发、高可用性解析

文章目录 引言一、高性能篇1.1 高性能的核心意义1.2 影响系统性能的因素1.3 高性能优化方法论1.3.1 读优化:缓存与数据库的结合1.3.2 写优化:异步化处理 1.4 高性能优化实践1.4.1 本地缓存 vs 分布式缓存1.4.2 数据库优化 二、高并发篇2.1 高并发的核心意…

吴恩达深度学习——有效运作神经网络

内容来自https://www.bilibili.com/video/BV1FT4y1E74V,仅为本人学习所用。 文章目录 训练集、验证集、测试集偏差、方差正则化正则化参数为什么正则化可以减少过拟合Dropout正则化Inverted Dropout其他的正则化方法数据增广Early stopping 归一化梯度消失与梯度爆…

基于RK3588/RK3576+MCU STM32+AI的储能电站电池簇管理系统设计与实现

伴随近年来新型储能技术的高质量规模化发展,储能电站作为新能源领域的重要载体, 旨在配合逐步迈进智能电网时代,满足电力系统能源结构与分布的创新升级,给予相应规模 电池管理系统的设计与实现以新的挑战。同时,电子系…

K8s 分布式存储后端(K8s Distributed Storage Backend)

K8s 分布式存储后端 在 K8s 中实现分布式存储后端对于管理跨集群的持久数据、确保高可用性、可扩展性和可靠性至关重要。在 K8s 环境中,应用程序通常被容器化并跨多个节点部署。虽然 K8s 可以有效处理无状态应用程序,但有状态应用程序需要持久存储来维护…

FFmpeg:多媒体处理的瑞士军刀

FFmpeg:多媒体处理的瑞士军刀 前言 FFmpeg 是一个功能强大且跨平台的开源多媒体框架,广泛应用于音视频处理领域。 它由多个库和工具组成,能够处理各种音视频格式,涵盖编码、解码、转码、流处理等多种操作。 无论是专业视频编辑…

unordered_map/set的哈希封装

【C笔记】unordered_map/set的哈希封装 🔥个人主页:大白的编程日记 🔥专栏:C笔记 文章目录 【C笔记】unordered_map/set的哈希封装前言一. 源码及框架分析二.迭代器三.operator[]四.使用哈希表封装unordered_map/set后言 前言 哈…

编程AI深度实战:大模型哪个好? Mistral vs Qwen vs Deepseek vs Llama

​​ 系列文章: 编程AI深度实战:私有模型deep seek r1,必会ollama-CSDN博客 编程AI深度实战:自己的AI,必会LangChain-CSDN博客 编程AI深度实战:给vim装上AI-CSDN博客 编程AI深度实战:火的编…

neo4j-community-5.26.0 install in window10

在住处电脑重新配置一下neo4j, 1.先至官方下载 Neo4j Desktop Download | Free Graph Database Download Neo4j Deployment Center - Graph Database & Analytics 2.配置java jdk jdk 21 官网下载 Java Downloads | Oracle 中国 path: 4.查看java -version 版本 5.n…

网络原理(5)—— 数据链路层详解

目录 一. 以太网 1.1 认识以太网 1.2 网卡与以太网 1.3 以太网帧格式 二. 认识MAC地址 三. MAC地址 与 IP地址 的区别 4.1 定义 4.2 分配方式 4.3 工作层次 4.4 地址格式 4.5 寻址方式 四. ARP协议 4.1 引入 4.2 ARP的概念 4.3 ARP工作原理 五. MTU 与 MSS …

【从零开始的LeetCode-算法】922. 按奇偶排序数组 II

给定一个非负整数数组 nums, nums 中一半整数是 奇数 ,一半整数是 偶数 。 对数组进行排序,以便当 nums[i] 为奇数时,i 也是 奇数 ;当 nums[i] 为偶数时, i 也是 偶数 。 你可以返回 任何满足上述条件的…

H264原始码流格式分析

1.H264码流结构组成 H.264裸码流(Raw Bitstream)数据主要由一系列的NALU(网络抽象层单元)组成。每个NALU包含一个NAL头和一个RBSP(原始字节序列载荷)。 1.1 H.264码流层次 H.264码流的结构可以分为两个层…

AI大模型(二)基于Deepseek搭建本地可视化交互UI

AI大模型(二)基于Deepseek搭建本地可视化交互UI DeepSeek开源大模型在榜单上以黑马之姿横扫多项评测,其社区热度指数暴涨、一跃成为近期内影响力最高的话题,这个来自中国团队的模型向世界证明:让每个普通人都能拥有媲…

图的基本术语——非八股文

我之前只看到了数据结构与算法的冰山一角,感觉这些术语只会让知识越来越难理解,现在来看,他们完美抽象一些概念和知识,非常重要。 本篇概念肯定总结不全,只有遇到的会写上,持续更新,之前文章已经…