加減乘除demo代碼
項目結構
CPP/
├── calculator.cpp
├── calculator.h
├── main.cpp
頭文件
#ifndef CALCULATOR_H
#define CALCULATOR_H#ifdef __cplusplus
extern "C" {#endifdouble add(double a, double b);double subtract(double a, double b);double multiply(double a, double b);double divide(double a, double b);#ifdef __cplusplus
}
#endif#endif // CALCULATOR_H
源文件
#include "calculator.h"
#include <iostream>double add(double a, double b) {return a + b;
}double subtract(double a, double b) {return a - b;
}double multiply(double a, double b) {return a * b;
}double divide(double a, double b) {if (b == 0) {std::cerr << "錯誤:除數不能為0!" << std::endl;return 0;}return a / b;
}
main.cpp測試
main.cpp,用于測試動態庫接口,需要在編譯好動態庫后進行測試
#include <iostream>
#include "calculator.h"int main() {double a = 8, b = 2;std::cout << "加法: " << add(a, b) << std::endl;std::cout << "減法: " << subtract(a, b) << std::endl;std::cout << "乘法: " << multiply(a, b) << std::endl;std::cout << "除法: " << divide(a, b) << std::endl;return 0;
}
編譯動態庫
-fPIC
:為共享庫生成位置無關代碼-shared
:生成共享庫-o libcalculator.so
:輸出為動態庫文件
g++ -fPIC -shared calculator.cpp -o libcalculator.so
查看動態庫有哪些接口
nm命令
nm -D libcalculator.so
帶有 T
的(代表函數定義在庫里)
信息比較多可以加grep命令進行過濾
objdump命令
objdump -T libcalculator.so
readelf命令
readelf -Ws libcalculator.so
main.cpp編譯
main.cpp會在編譯的過程中去動態鏈接libcalculator.so
-L.
:告訴編譯器去當前目錄查找庫文件-lcalculator
:鏈接名為libcalculator.so
的庫(省略前綴lib
和擴展名.so
)
g++ main.cpp -L. -lcalculator -o main
執行測試程序
運行程序前,設置動態庫路徑
LD_LIBRARY_PATH
是 Linux 下指定“運行時動態庫查找路徑”的環境變量.
代表當前目錄$LD_LIBRARY_PATH
是保留已有路徑
#設置環境變量
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
上面代碼的意思是:請系統在當前目錄找動態庫,然后再去默認路徑找
系統默認會查找這些路徑
- /lib
- /usr/lib
永久添加路徑
更新系統的動態鏈接庫緩存,也就是告訴系統有哪些 .so
庫、它們在哪兒,以便程序運行時可以找到這些庫。
sudo ldconfig
什么時候用?
- 安裝了新的
.so
動態庫 - 把
.so
文件放到了非標準路徑下(比如/usr/local/lib
、/opt/mylibs
) - 修改了
/etc/ld.so.conf
或/etc/ld.so.conf.d/
下的配置
運行程序
./main
運行程序前,為什么要設置動態庫路徑
因為你用的是 動態鏈接庫(**.so**
** 文件),程序運行時需要知道這個庫在哪里**。編譯的時候你告訴了編譯器怎么找到它(-L.
),但運行時要靠系統的**動態鏈接器(ld.so
)**來加載 .so
文件。
如果 .so
不在系統默認的路徑里(比如 /lib
, /usr/lib
),就需要你手動告訴它去哪里找。
python調用
test_calculator.py
import ctypes
import os# 加載動態庫
lib_path = os.path.abspath("libcalculator.so")
calculator = ctypes.CDLL(lib_path)# 設置參數和返回類型
calculator.add.argtypes = [ctypes.c_double, ctypes.c_double]
calculator.add.restype = ctypes.c_doublecalculator.subtract.argtypes = [ctypes.c_double, ctypes.c_double]
calculator.subtract.restype = ctypes.c_doublecalculator.multiply.argtypes = [ctypes.c_double, ctypes.c_double]
calculator.multiply.restype = ctypes.c_doublecalculator.divide.argtypes = [ctypes.c_double, ctypes.c_double]
calculator.divide.restype = ctypes.c_double# 測試調用
a = 10.0
b = 2.0print("加法:", calculator.add(a, b))
print("減法:", calculator.subtract(a, b))
print("乘法:", calculator.multiply(a, b))
print("除法:", calculator.divide(a, b))
測試調用
- export LD_LIBRARY_PATH=CPP:$LD_LIBRARY_PATH
- python3 test_calculator.py
Makefile生成動態庫
# Makefile for building libcalculator.so shared library# 編譯器
CXX = g++# 編譯選項
CXXFLAGS = -fPIC -Wall -Wextra -O2# 目標名稱
TARGET = libcalculator.so# 源碼文件
SRC = calculator.cpp# 頭文件
HEADER = calculator.h# 輸出路徑(當前目錄)
OBJ = $(SRC:.cpp=.o)# 默認目標
all: $(TARGET)# 生成共享庫
$(TARGET): $(OBJ)$(CXX) -shared -o $@ $^# 編譯cpp為.o文件
%.o: %.cpp $(HEADER)$(CXX) $(CXXFLAGS) -c $< -o $@# 清理生成的文件
clean:rm -f $(OBJ) $(TARGET).PHONY: all clean
運行make
Makefile一起生成動態庫和可執行程序
# 編譯器
CXX = g++# 編譯選項
CXXFLAGS = -fPIC -Wall -Wextra -O2# 生成共享庫的目標
LIB_NAME = libcalculator.so# 可執行文件名
EXEC = main# 源文件
LIB_SRC = calculator.cpp
MAIN_SRC = main.cpp# 對應的中間目標
LIB_OBJ = $(LIB_SRC:.cpp=.o)
MAIN_OBJ = $(MAIN_SRC:.cpp=.o)# 默認目標
all: $(LIB_NAME) $(EXEC)# 生成共享庫
$(LIB_NAME): $(LIB_OBJ)$(CXX) -shared -o $@ $^# 編譯 main,鏈接動態庫
$(EXEC): $(MAIN_OBJ) $(LIB_NAME)$(CXX) -o $@ $(MAIN_OBJ) -L. -lcalculator# 通用規則:.cpp -> .o
%.o: %.cpp$(CXX) $(CXXFLAGS) -c $< -o $@# 運行程序
run: allLD_LIBRARY_PATH=. ./$(EXEC)# 清理
clean:rm -f *.o $(LIB_NAME) $(EXEC).PHONY: all run clean
運行make
用CMakeLists管理
項目結構
CPP/
├── calculator.cpp
├── calculator.h
├── main.cpp
└── CMakeLists.txt
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(CalculatorProject)# 設置 C++ 標準
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)# 輸出到 build 目錄下的 bin 和 lib 子目錄
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)# 添加庫(生成 libcalculator.so)
add_library(calculator SHARED calculator.cpp)# 添加可執行文件
add_executable(main main.cpp)# 鏈接共享庫
target_link_libraries(main PRIVATE calculator)# 設置頭文件搜索路徑
target_include_directories(main PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(calculator PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
使用方法
- mkdir build && cd build
- cmake …
- cmake --build .
目錄結構
CPP/
├── calculator.cpp
├── calculator.h
├── main.cpp
├── CMakeLists.txt
└── build/
├── bin/│ └── main└── lib/└── libcalculator.so