自学内容网 自学内容网

static 函数默认链接不可见

目录

发现

起初是在看编译器学习仓库的第一章 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_H

extern int calc_errno;

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 defined
    6 | 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

原文地址:https://blog.csdn.net/Don_tknowwhat/article/details/142726294

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!