static 函数默认链接不可见

ops/2024/10/8 22:32:23/

目录

  • 发现
  • 验证

发现

起初是在看编译器学习仓库的第一章 README 和代码的时候发现的,头文件中的有一些函数被声明为 static 的,有一些不是。我依稀记得以前看八股文的时候,带 static 声明的函数其可见性仅限于当前的编译翻译单元,也就是编译器在进行词法分析和语法分析的时候,这里面产出的符号不会用于其他文件编译翻译时候的查找和链接了,不存在于编译产物的导出符号表中。

简单谷歌了一下看看人家怎么说的:

In C and C++, function declarations are extern by default. You’ve likely been using extern functions often without realizing it! Any time you call a function defined in another source file, you’re relying on its implicit extern declaration. In this example, we declare the add function in the math.

这是说,对于大部分的编译工具链来说,函数的声明默认是 extern 属性的,

  • extern 表示该函数或变量在其他地方定义,并且可以被多个文件共享。

  • static 表示该函数或变量仅在当前文件(或翻译单元)中可见,不能被其他文件访问。

  • externstatic 关键字的作用是相反的,不能同时使用。

  • 如果你在头文件中声明了一个函数为 extern,然后在源文件中将其声明为 static,会导致编译错误。

通常我在写项目的时候,只会对于头文件中的全局共享变量,比如全局错误代码 errno 使用 extern 属性,避免其他文件包含该头文件相当于声明了 errno 之后,又链接了其他包含该头文件的源文件造成符号重定义。但是我从来没有考虑过,原来函数默认是 导出的 是因为编译器默认帮我们携带了 extern 关键字。那么对应的,源文件(头文件)中的变量应该默认是 static 仅当前翻译编译单元可见。

验证

写了三个简单的文件测试 static 属性函数的链接行为。首先是头文件声明函数和变量:

// calc.h
#ifndef CALC_H
#define CALC_Hextern int calc_errno;static int inner_add(int a, int b);int add(int a, int b);#endif

然后是实现了该头文件声明的函数的源文件:

// calc.cpp
#include <calc.h>int calc_errno = 0;static int inner_add(int a, int b)
{if (a < 0 || b < 0)calc_errno = 1;return a + b;
}int add(int a, int b)
{return inner_add(a, b);
}

这两个文件将在 CMakeLists.txt 中声明为一个动态库目标(SHARED),然后 main.cpp 在编译翻译为目标代码之后,再尝试链接这个动态库,看看找不找得到使用 static 函数的定义符号引用。

// main.cpp
#include <iostream>
#include <calc.h>
using namespace std;int main(int argc, char** argv)
{cout << "hello world" << endl;cout << inner_add(-1, -1) << endl;cout << calc_errno << endl;return 0;
}

另外,所使用的 CMakeLists.txt 代码如下:

cmake_minimum_required(VERSION 3.20)
project(static)message("${CMAKE_CURRENT_SOURCE_DIR}")
message("${CMAKE_CURRENT_BINARY_DIR}")
message("${CMAKE_BUILD_TYPE}")set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE})include_directories(.)add_library(calc SHARED calc.cpp calc.h)add_executable(main main.cpp)
target_link_libraries(main calc)

在 VScode 中调用 CMake 完成配置、编译、链接,得到的终端输出如下:

 *  Executing task: CMake: Configure Config task started...
/home/fredom/workspace/test/cc/static_link
/home/fredom/workspace/test/cc/static_link/build
Debug
Not searching for unused variables given on the command line.
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fredom/workspace/test/cc/static_link/build
Configure finished with return code 0*  Terminal will be reused by tasks, press any key to close it. *  Executing task: CMake: Build build task started....
/home/fredom/bin/cmake/cmake --build /home/fredom/workspace/test/cc/static_link/build --config Debug --target all -j 22 --
[ 25%] Building CXX object CMakeFiles/calc.dir/calc.cpp.o
[ 50%] Linking CXX shared library bin/Debug/libcalc.so
[ 50%] Built target calc
[ 75%] Building CXX object CMakeFiles/main.dir/main.cpp.o
In file included from /home/fredom/workspace/test/cc/static_link/main.cpp:2:
/home/fredom/workspace/test/cc/static_link/./calc.h:6:12: warning: ‘int inner_add(int, int)’ used but never defined6 | static int inner_add(int a, int b);|            ^~~~~~~~~
[100%] Linking CXX executable bin/Debug/main
/usr/bin/ld: CMakeFiles/main.dir/main.cpp.o: in function `main':
/home/fredom/workspace/test/cc/static_link/main.cpp:9:(.text+0x49): undefined reference to `inner_add(int, int)'
collect2: error: ld returned 1 exit status
gmake[2]: *** [CMakeFiles/main.dir/build.make:98: bin/Debug/main] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:111: CMakeFiles/main.dir/all] Error 2
gmake: *** [Makefile:91: all] Error 2
build finished with error(s).

可以看到,main.cpp 如果直接使用头文件中的 static 属性函数,那么是无法链接成功的(undefined reference to xxx)。换成 add 函数的话,编译就没问题可以通过了。

 *  Executing task: CMake: Configure Config task started...
/home/fredom/workspace/test/cc/static_link
/home/fredom/workspace/test/cc/static_link/build
Debug
Not searching for unused variables given on the command line.
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fredom/workspace/test/cc/static_link/build
Configure finished with return code 0*  Terminal will be reused by tasks, press any key to close it. *  Executing task: CMake: Build build task started....
/home/fredom/bin/cmake/cmake --build /home/fredom/workspace/test/cc/static_link/build --config Debug --target all -j 22 --
[ 50%] Built target calc
[ 75%] Building CXX object CMakeFiles/main.dir/main.cpp.o
[100%] Linking CXX executable bin/Debug/main
[100%] Built target main
build finished successfully.

此时运行编译链接成功的 main ELF 二进制可执行文件,得到输出:

hello world
-2
1
[1] + Done

http://www.ppmy.cn/ops/122833.html

相关文章

Hive优化操作(二)

Hive 数据倾斜优化 在使用 Hive 进行大数据处理时&#xff0c;数据倾斜是一个常见的问题。本文将详细介绍数据倾斜的概念、表现、常见场景及其解决方案。 1. 什么是数据倾斜&#xff1f; 数据倾斜是指由于数据分布不均匀&#xff0c;导致大量数据集中到某个节点或任务中&…

鸿蒙跨端实践-JS虚拟机架构实现

作者&#xff1a;京东科技 杜强强 前言 在Roma跨端方案中&#xff0c;JS虚拟机是框架的核心&#xff0c;负责执行动态化的JS代码。在Android平台采用了基于V8的J2V8&#xff0c;iOS平台则使用了系统自带的JSCore&#xff0c;而在HarmonyOS中&#xff0c;由于业界无类似的框架&a…

费曼学习法没有输出对象怎么办?

‌费曼学习法并不需要输出对象。‌费曼学习法的核心在于通过将所学知识以简明易懂的方式解释给自己听&#xff0c;从而加深对知识的理解和记忆。这种方法强调的是理解和反思的过程&#xff0c;而不是简单地通过输出&#xff08;如向他人解释&#xff09;来检验学习效果。费曼学…

StarRocks 中如何做到查询超时(QueryTimeout)

背景 本文基于 StarRocks 3.1.7 主要是分析以下两种超时设置的方式: SESSION 级别 SET query_timeout 10;SELECT sleep(20);SQL 级别 select /* SET_VAR(query_timeout10) */ sleep(20); 通过本文的分析大致可以了解到在Starrocks的FE端是如何进行Command的交互以及数据流走…

C++版iwanna1

第一篇目录 开头程序Game.cpp源文件Player.h头文件Player.cpp源文件trigger.h头文件trigger.cpp源文件Cmp.h头文件Cmp.cpp源文件 开头 大家好&#xff0c;我叫这是我58。 程序 Game.cpp源文件 #define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> #include <c…

智能机器是世界上的新物种

智能机器是世界上的新物种 1、为什么智能机器是世界上的新物种&#xff1f; 这个新物种的第一个生命体是1946年的美国的第一台电子计算机。 生命体的两个基本特征是繁殖和新陈代谢 电子计算机的繁殖方面采用的是无性繁殖&#xff0c;也就是像病毒一样&#xff0c; 一分为二&a…

举例说明 .Net Core 单元测试中 xUnit 的 [Theory] 属性的用法

在这篇文章中&#xff0c;我们探讨了如何使用 xUnit 的 [Theory] 属性来运行参数化测试。通过示例展示了如何使用 [InlineData]、[MemberData] 和 [ClassData] 提供不同的数据源&#xff0c;从而简化测试代码并提高测试覆盖率。这些方法有助于在 .NET 应用程序中进行更有效的单…

JVM 系列知识体系全面回顾

经过几个月的努力&#xff0c;JVM 知识体系终于梳理完成了。 很早之前也和小伙伴们分享过 JVM 相关的技术知识&#xff0c;再次感谢大家支持和反馈。 最后再次献上 JVM系列文章合集索引&#xff0c;感兴趣的小伙伴可以点击查看。 JVM系列(一) -什么是虚拟机JVM系列(二) -类的…