c++ 写一个c静态链接库 供c或c++项目调用

静态链接库:输出的库文件及头文件提供给调用方之后,会随着编译将代码拷贝到调用者处;优点是无依赖;缺点是,静态库有更新后,所有可执行文件需要重新链接。
动态链接库:优点:编译时不需要拷贝代码,运行时动态链接,这样比较节省资源。缺点:运行时动态加载,可能影响程序执行性能。
本节通过CLion编辑器,创建c静态链接库;后通过分别给c和c++控制台项目,以达到了解静态链接库的创建和使用的基本流程。

创建c静态链接库项目

# 头文件 library.h
#ifndef CSTATICLIB_LIBRARY_H
#define CSTATICLIB_LIBRARY_H

typedef void (*callback)(void);

void add_handler(callback event);

void raise_event();

#endif // CSTATICLIB_LIBRARY_H

# library.c文件
#include "library.h"

#include <stdio.h>

callback close;

void add_handler(callback event) {
    close = event;
}

void raise_event() {
    if (close != NULL) {
        close();
    }
}

CMakeLists.txt文件配置

cmake_minimum_required(VERSION 4.0)
project(cstaticlib C)

set(CMAKE_C_STANDARD 11)

add_library(cstaticlib STATIC library.c)

编译项目得到静态库文件

创建c可执行控制台项目

创建可执行c控制台项目,用来测试如何使用静态链接库。

引入库文件和静态库文件

创建include目录和lib目录,将静态库文件libcstaticlib.a文件拷贝到lib目录,将library.h文件拷贝到include目录下。
引入静态库文件和头文件

配置CMakeLists.txt 配置文件查找路径 链接库文件

直接在main.c文件中引入library.h头文件是会报错的,因为静态库需要将静态库文件在链接阶段和本可执行项目合并,所以需要在CMake配置文件中指定要链接哪些库;

cmake_minimum_required(VERSION 4.0)
project(testcdemo C)

set(CMAKE_C_STANDARD 11)

#设置链接库查找路径
link_directories(${PROJECT_SOURCE_DIR}/lib)

#设置头文件查找路径
include_directories(${PROJECT_SOURCE_DIR}/include)

add_executable(testcdemo main.c)

#指定要链接哪个库
#target_link_libraries(${PROJECT_NAME} libcstaticlib.a)
#库文件一般都是libxxx.a或者libxxx.lib这种格式,所以可以将文件后缀和lib前缀省略。这样写跨平台性好。
target_link_libraries(${PROJECT_NAME} cstaticlib)

编译并运行项目

在main函数中写入下面代码,编译并调试代码运行情况:

#include <stdio.h>
#include <library.h>

void onclose();

int main(void) {
    callback event = onclose;
    add_handler(event);
    raise_event();
    return 0;
}


void onclose() {
    printf("Closing...\n");
}

输出

Closing...

以上便是,c可执行项目中调用静态链接库项目。

创建c++可执行控制台项目

引入库文件和头文件、配置CMakeLists.txt、编写代码步骤同上面操作;但编译报错:

FAILED: teststaticlib 
: && /usr/bin/c++ -g -arch arm64 -Wl,-search_paths_first -Wl,-headerpad_max_install_names  CMakeFiles/teststaticlib.dir/main.cpp.o -o teststaticlib -L/Users/a77/Code/teststaticlib/lib -Wl,-rpath,/Users/a77/Code/teststaticlib/lib  -lcstaticlib && :
Undefined symbols for architecture arm64:
  "add_handler(void (*)())", referenced from:
      _main in main.cpp.o
     (found _add_handler in /Users/a77/Code/teststaticlib/lib/libcstaticlib.a(library.c.o), declaration possibly missing extern "C")
  "raise_event()", referenced from:
      _main in main.cpp.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

add_handler函数被发现变成了_add_handler,这个属于c++为照顾c语言不支持函数重载所设计的名称管理机制,为的是函数名不重复;
windows平台可以使用VisualStudio提供的dumpbin.exe /EXPORTS xxx.dll 查看函数导出符号;macos可以使用 nm xxx.dylib 或者 otool -tV xxx.dylib 来查看

a77@iMac Code % nm /Users/a77/Code/dynamiclib/cmake-build-debug/libdynamiclib.dylib
0000000000003f50 T _add_handler
0000000000004000 S _close
0000000000003f70 T _raise_event
a77@iMac Code % otool -tV /Users/a77/Code/dynamiclib/cmake-build-debug/libdynamiclib.dylib
/Users/a77/Code/dynamiclib/cmake-build-debug/libdynamiclib.dylib:
(__TEXT,__text) section
_add_handler:
0000000000003f50	sub	sp, sp, #0x10
0000000000003f54	str	x0, [sp, #0x8]
0000000000003f58	ldr	x8, [sp, #0x8]
0000000000003f5c	adrp	x9, 1 ; 0x4000
0000000000003f60	add	x9, x9, #0x0
0000000000003f64	str	x8, [x9]
0000000000003f68	add	sp, sp, #0x10
0000000000003f6c	ret
_raise_event:
0000000000003f70	stp	x29, x30, [sp, #-0x10]!
0000000000003f74	mov	x29, sp
0000000000003f78	adrp	x8, 1 ; 0x4000
0000000000003f7c	add	x8, x8, #0x0
0000000000003f80	ldr	x8, [x8]
0000000000003f84	subs	x8, x8, #0x0
0000000000003f88	cset	w8, eq
0000000000003f8c	tbnz	w8, #0x0, 0x3fa8
0000000000003f90	b	0x3f94
0000000000003f94	adrp	x8, 1 ; 0x4000
0000000000003f98	add	x8, x8, #0x0
0000000000003f9c	ldr	x8, [x8]
0000000000003fa0	blr	x8
0000000000003fa4	b	0x3fa8
0000000000003fa8	ldp	x29, x30, [sp], #0x10
0000000000003fac	ret
a77@iMac Code % 

从上面的输出信息可以看到 函数名确实如报错信息中所述,前面加了下划线;好在,编译器提示我们要使用 extern "C"这样的代码块解决问题:
代码如下:

#include <csignal>
#include <iostream>

extern "C" {
#include <library.h>
}

void action();


int main() {
    callback func = action;
    add_handler(func);
    raise_event();

    return 0;
}

void action() {
    std::cout << "receive event" << std::endl;
}

好了,以上便是从创建静态库项目和分别使用c和c++控制台项目引用静态库的全过程记录。

posted @ 2025-11-01 13:00  BigBosscyb  阅读(15)  评论(0)    收藏  举报