12. c++基础知识回顾(1)

很简单的东西 看了时间长用不到就容易忘 但面试总归要问些什么

怎么办 那就再看一遍

二、C++对C语言增强的地方

C++语言完全兼容C语言,并且在C语言的基础上,扩充了一些语法,从而更方便进行软件开发。

命名空间

引用

重载

函数重载

运算符重载

面向对象特性

封装

继承

多态

泛型编程

异常处理

标准模板库STL

C++多文件命名空间完整实现

按照题目要求分为4个文件:space.h(命名空间声明)、a.cpp(加法calc实现)、b.cpp(减法calc实现)、main.cpp(调用测试)

1. space.h 头文件(命名空间函数声明)

cpp 复制代码
#ifndef SPACE_H
#define SPACE_H

// 命名空间A_Space 函数声明
namespace A_Space
{
    int calc(int a, int b);
}

// 命名空间B_Space 函数声明
namespace B_Space
{
    int calc(int a, int b);
}

#endif

说明:头文件保护宏防止重复包含,仅做函数声明,不写实现

2. a.cpp(A_Space加法实现)

cpp 复制代码
#include "space.h"

namespace A_Space
{
    // 加法运算
    int calc(int a, int b)
    {
        return a + b;
    }
}

3. b.cpp(B_Space减法实现)

cpp 复制代码
#include "space.h"

namespace B_Space
{
    // 减法运算
    int calc(int a, int b)
    {
        return a - b;
    }
}

4. main.cpp(调用两个命名空间函数)

cpp 复制代码
#include <iostream>
#include "space.h"

using namespace std;

int main()
{
    int x = 10, y = 3;

    // 方式1:完整域名访问(推荐,无命名冲突)
    int add_res = A_Space::calc(x, y);
    int sub_res = B_Space::calc(x, y);

    cout << "A_Space加法结果:" << add_res << endl;
    cout << "B_Space减法结果:" << sub_res << endl;

    return 0;
}

编译运行指令(g++)

bash 复制代码
g++ main.cpp a.cpp b.cpp -o calc_demo
./calc_demo

输出结果

复制代码
A_Space加法结果:13
B_Space减法结果:7

核心知识点说明 💡

  1. 命名空间作用 :解决同名函数calc冲突,不同命名空间下的同名函数相互隔离
  2. 头文件与源文件分离
    • .h:仅放函数/类声明 ,供外部文件#include调用
    • .cpp:包含对应头文件,实现命名空间内函数逻辑
  3. 访问命名空间成员命名空间名::函数名(参数),不建议同时using namespace A_Space; using namespace B_Space;,会造成calc歧义编译报错

四、对指针和数组的引用(C/C++ 完整知识点讲解)

一、基础概念区分

1. 数组本质

数组名是数组首元素的常量指针,不可被重新赋值;数组在内存中是一段连续同类型内存空间。

c 复制代码
int arr[5] = {1,2,3,4,5};
// arr == &arr[0],代表首元素地址

2. 指针本质

指针是存储内存地址的变量,可自由修改指向,能指向数组、普通变量、堆内存。

c 复制代码
int* p = arr; // 指针p指向数组首元素

3. 引用本质(C++独有)

引用是变量的别名 ,必须在定义时初始化,不能为空、不能改指向;分为普通变量引用、指针引用、数组引用三类。


二、数组的引用(核心语法)

1. 定长数组引用语法

格式:类型 (&引用名)[数组长度] = 数组名;

cpp 复制代码
int arr[5] = {10,20,30,40,50};
// 给长度为5的int数组起别名ref_arr
int (&ref_arr)[5] = arr; 

ref_arr[0] = 99; // 修改引用等价修改原数组

限制:引用数组时必须显式写明数组长度,长度不匹配会编译报错。

2. 模板适配任意长度数组引用(通用写法)

cpp 复制代码
template<typename T, size_t N>
void printArr(T (&arr)[N]){
    for(size_t i=0; i<N; i++){
        cout << arr[i] << " ";
    }
}
// 调用:自动推导数组长度
int a[3] = {1,2,3};
printArr(a);

三、指针的引用(指针别名)

语法:类型* (&指针引用名) = 原指针;

cpp 复制代码
int num = 100;
int* p = &num;
// p_ref是指针p的别名
int* (&p_ref) = p;

*p_ref = 200; // 等价于*p=200,修改num的值
int b = 20;
p_ref = &b; // 等价p=&b,修改原指针的指向

易错区分

  • int &* p:非法语法,不存在指向引用的指针
  • int* & p:合法,指针的引用(别名)

四、指针、数组、引用三者访问等价关系

对数组 int arr[5]、指针 int* p=arr、数组引用 int (&ref)[5]=arr

以下写法完全等价:

cpp 复制代码
arr[2] == *(arr + 2)
p[2]   == *(p + 2)
ref[2] == *(ref + 2)

内存访问原理:地址偏移 sizeof(int) * 下标


五、完整演示代码(可直接运行)

cpp 复制代码
#include <iostream>
using namespace std;

// 模板函数:接收任意长度数组引用
template<typename T, size_t N>
void show(T (&arr)[N]){
    for(int i=0; i<N; i++) cout << arr[i] << " ";
    cout << endl;
}

int main(){
    // 1. 数组引用演示
    int arr[4] = {1,3,5,7};
    int (&ref_arr)[4] = arr;
    ref_arr[1] = 99;
    cout << "数组引用修改后:";
    show(arr);

    // 2. 指针引用演示
    int val = 10;
    int* p = &val;
    int* (&ref_p) = p;
    *ref_p = 999;
    cout << "指针引用修改变量:" << val << endl;

    // 3. 指针指向数组 + 指针引用
    int* p_arr = arr;
    int* (&rp) = p_arr;
    cout << "指针引用访问数组:" << rp[2] << endl;
    return 0;
}

输出结果

复制代码
数组引用修改后:1 99 5 7 
指针引用修改变量:999
指针引用访问数组:5

六、常见易错点 💡

  1. 数组引用长度必须匹配
    int arr[3]; int (&r)[4]=arr; 直接编译失败。
  2. 数组名是常量指针,不能赋值
    arr = p; 非法;但数组引用 ref_arr 可读写元素,仅不能修改数组首地址。
  3. 引用不能悬空、不能二次绑定
    定义时必须绑定变量,绑定后无法更换绑定对象。
  4. C语言无引用语法,引用仅C++支持。
  5. 区分:
    • 数组指针:int (*p)[5] → 指向整个数组的指针
    • 数组引用:int (&r)[5] → 数组的别名

C++引用作为函数参数、返回值完整讲解

五、引用作为函数参数

1. 语法格式

cpp 复制代码
void func(int &ref) {
    ref = 100; // ref是实参的别名,直接操作原变量
}

int main() {
    int a = 10;
    func(a); // 传变量本身,不需要取地址&
    // 执行后 a = 100
    return 0;
}

2. 核心特点

  1. 操作原变量,无拷贝
    引用是变量别名,函数内对引用的修改会直接作用于外部实参,不需要指针解引用,语法更简洁。
  2. 省去值拷贝开销
    传递自定义类、大数组时,相比值传递不会复制完整对象,大幅提升性能。
  3. 必须传入可修改左值
    • 不能传入常量、表达式、临时值:func(5) / func(a+1) 编译报错;
    • 若仅只读不修改,推荐使用const引用void func(const int &ref),此时可传常量、临时值。

3. 对比三种传参方式

传参方式 拷贝对象 能否修改原值 传临时常量 语法复杂度
值传递 完整拷贝 不能 支持 简单
指针传递 拷贝地址 能(需*解引用) 不支持字面量 繁琐(&/*)
普通引用 无拷贝 不支持 简洁
const引用 无拷贝 不能 支持 推荐只读场景

4. 典型使用场景

  • 需要在函数内修改外部变量(替代指针);
  • 传递大型自定义结构体/类,避免拷贝损耗;
  • 函数只读参数,使用const &兼顾性能与通用性。

六、引用作为函数返回值

1. 基础语法

cpp 复制代码
int g_num = 10;
// 返回全局变量的引用
int& getNum() {
    return g_num;
}

int main() {
    int& r = getNum();
    r = 99; // 直接修改全局g_num
    // 链式调用:getNum() = 200; 合法
    return 0;
}

2. 核心规则(⚠️ 高危禁忌)

禁止返回局部变量引用

局部变量存储在栈,函数执行完毕后栈内存释放,返回的引用会指向已销毁内存,产生野引用、未定义行为:

cpp 复制代码
// 错误示例!
int& badFunc() {
    int temp = 10; // 局部栈变量
    return temp;
}
合法返回对象
  1. 全局变量 / 静态局部变量(生命周期贯穿程序);
  2. 传入函数的引用参数(原变量生命周期更长);
  3. 类成员函数返回自身*this(实现运算符重载链式调用)。

3. 核心优势

  1. 支持链式运算
    最经典应用:operator<<operator=重载

    cpp 复制代码
    class Test {
    public:
        int val;
        Test& operator=(const Test &t) {
            val = t.val;
            return *this; // 返回自身引用,支持 a=b=c
        }
    };
  2. 无拷贝开销
    返回对象时不会生成临时副本,性能优于值返回。

4. const引用返回

若不需要外部修改返回对象,加const保护:

cpp 复制代码
const int& getVal() {
    return g_num;
}
// 此时 getVal() = 100; 编译报错,禁止修改

5. 典型场景

  • 运算符重载(赋值、流运算符链式调用);
  • 容器类返回内部元素读写引用(vector::operator[]);
  • 需要函数调用表达式作为左值赋值。

两者核心总结对比

维度 引用作函数参数 引用作函数返回值
核心作用 减少拷贝、修改外部变量 支持左值赋值、链式调用、减少返回拷贝
生命周期风险 无风险(外部变量一定存活) 高风险,严禁返回局部栈变量
const修饰作用 允许传入临时常量,禁止修改 禁止外部通过返回值修改对象
经典案例 交换函数void swap(int &a,int &b) 赋值运算符重载、数组下标[]

💡开发建议

  1. 只读参数一律用const &,兼顾性能与兼容性;
  2. 输出型参数优先引用,比指针代码更简洁安全;
  3. 返回引用时严格校验返回对象生命周期,杜绝局部变量引用;
  4. 类设计中返回成员引用时,按需加const区分读写接口。

my_swap 两种实现:指针版 + 引用版(C++)

一、引用实现(对应"引用作为函数参数"知识点)

原理

引用是变量别名,函数内直接操作原变量,无需取地址、解引用,代码简洁。

cpp 复制代码
#include <iostream>
using namespace std;

// 引用作参数
void my_swap_ref(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

int main()
{
    int x = 10, y = 20;
    cout << "交换前:x=" << x << " y=" << y << endl;
    my_swap_ref(x, y); // 直接传变量,不用&
    cout << "交换后:x=" << x << " y=" << y << endl;
    return 0;
}

二、指针实现(C/C++通用)

原理

传入变量地址,函数内通过指针解引用修改原始内存数据。

cpp 复制代码
#include <iostream>
using namespace std;

// 指针作参数
void my_swap_ptr(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main()
{
    int x = 10, y = 20;
    cout << "交换前:x=" << x << " y=" << y << endl;
    my_swap_ptr(&x, &y); // 传变量地址,必须加&
    cout << "交换后:x=" << x << " y=" << y << endl;
    return 0;
}

三、完整合并代码(同时演示两种写法)

cpp 复制代码
#include <iostream>
using namespace std;

// 1. 引用版交换
void my_swap_ref(int &a, int &b)
{
    int t = a;
    a = b;
    b = t;
}

// 2. 指针版交换
void my_swap_ptr(int *a, int *b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

int main()
{
    int num1 = 5, num2 = 8;
    cout << "===== 引用交换测试 =====" << endl;
    cout << "交换前:" << num1 << " " << num2 << endl;
    my_swap_ref(num1, num2);
    cout << "交换后:" << num1 << " " << num2 << endl;

    // 重置数值
    num1 = 5; num2 = 8;
    cout << "\n===== 指针交换测试 =====" << endl;
    cout << "交换前:" << num1 << " " << num2 << endl;
    my_swap_ptr(&num1, &num2);
    cout << "交换后:" << num1 << " " << num2 << endl;

    return 0;
}

四、输出结果

复制代码
===== 引用交换测试 =====
交换前:5 8
交换后:8 5

===== 指针交换测试 =====
交换前:5 8
交换后:8 5

五、两者核心区别对比

对比项 引用实现 my_swap_ref 指针实现 my_swap_ptr
函数形参 int &a 引用 int *a 指针
调用传参 直接传变量 my_swap_ref(x,y) 取地址传参 my_swap_ptr(&x,&y)
内部操作 直接使用 ab 需要解引用 *a*b
空值风险 无引用空值,更安全 可能传入空指针 nullptr,会崩溃
适用语言 仅C++支持 C、C++通用

💡关键知识点

  1. 如果只写 void swap(int a,int b)(值传递),函数内只是拷贝副本,无法修改外部变量,交换失效;
  2. 引用本质是指针的语法糖,底层实现和指针类似,但对外语法更简单安全;
  3. 引用参数必须绑定合法左值,不能传数字常量、表达式。

C++ 左值(lvalue)、右值(rvalue)完整讲解(C++11及以后标准)

一、最简单一句话区分

能取地址 & 的是左值;不能取地址、用完就销毁的临时数据是右值。

赋值规则:等号左边只能放左值,不能放纯右值

1. 左值 lvalue(locator value,有内存位置的值)

核心特征

  1. 拥有持久内存地址 ,可以用 & 取地址;
  2. 生命周期长,不会表达式结束立刻销毁;
  3. 可以出现在赋值运算符 = 的左边;
  4. 分为可修改左值const左值

常见左值例子

cpp 复制代码
int a = 10;
int arr[5] = {1,2,3};
int &ref = a;
const int b = 20;

a;        // 变量a,左值
arr[0];   // 数组元素,左值
ref;      // 引用,左值
b;        // const常量,const左值(不能修改,但仍是左值,可&b)

函数返回左值引用也是左值:

cpp 复制代码
int g = 100;
int& getG() { return g; }
getG() = 200; // 合法,返回左值,可以赋值

const左值特点

能取地址,但不能修改

cpp 复制代码
const int c = 5;
&c;     // 合法
c = 10; // 报错,const左值不可修改

2. 右值 rvalue(没有持久内存,临时值)

C++11 把右值细分为两类:纯右值 prvalue + 将亡值 xvalue

2.1 纯右值 prvalue(pure rvalue,传统意义上的右值)

特征
  • 临时对象、字面量,无稳定内存地址,不能 & 取地址
  • 表达式执行完直接销毁;
  • 不能放在 = 左侧。
纯右值例子
cpp 复制代码
10;            // 数字字面量
3 + 5;         // 运算临时结果
std::string("hello"); // 临时字符串对象
int func() { return 1; }
func();        // 返回普通值,纯右值

错误示范:

cpp 复制代码
10 = a;  // 编译报错,纯右值不能赋值
&(3+5);  // 报错,不能取纯右值地址

2.2 将亡值 xvalue(expiring value,即将销毁的对象)

C++11 新增,属于右值的一种,代表可以被移动的临时对象

产生场景
  1. std::move(左值) 转换后的结果;
  2. 返回右值引用 T&& 的函数调用。
cpp 复制代码
int num = 10;
std::move(num);       // xvalue 将亡值
int&& getR() {
    return std::move(num);
}
getR();               // xvalue

特点:

  • 属于右值,但底层拥有资源,可以移动语义(避免拷贝);
  • 同样不能放在等号左边。

3. C++ 值分类整体层级(标准定义)

复制代码
表达式的值类别
├─ 左值 lvalue
│  └─ const lvalue
└─ 右值 rvalue
   ├─ 纯右值 prvalue(字面量、普通函数返回值)
   └─ 将亡值 xvalue(move转换、T&&返回)

4. 引用绑定规则(重点,区分左值引用/右值引用)

1)普通左值引用 T&

只能绑定左值,不能绑定右值

cpp 复制代码
int a = 10;
int &r1 = a;      // OK,绑定左值
int &r2 = 10;     // 报错,不能绑定纯右值
int &r3 = std::move(a); // 报错,不能绑定xvalue

2)const左值引用 const T&

万能引用,左值、纯右值、将亡值全都能绑定

cpp 复制代码
const int &r1 = a;
const int &r2 = 10;
const int &r3 = std::move(a);

函数参数常用它接收任意值,避免拷贝。

3)右值引用 T&&

只能绑定右值(prvalue / xvalue),不能绑定普通左值

cpp 复制代码
int &&r1 = 10;            // OK 纯右值
int &&r2 = std::move(a);  // OK 将亡值
int &&r3 = a;             // 报错,不能绑定左值

作用:实现移动构造、移动赋值,减少内存拷贝开销。

5. 通俗记忆总结

  1. 左值 = 有名字、有地址、能长期存在的变量/引用
    变量、数组元素、左值引用、const变量都是左值;
  2. 纯右值 = 无名临时数据
    数字、字符串字面量、算术运算结果、返回值为普通类型的函数;
  3. 将亡值 = 被move包装后的左值
    属于右值,专门用来触发移动语义;
  4. 赋值左边只能放可修改左值,所有右值都不能赋值;
  5. 取地址 & 只能作用于左值,右值取地址编译报错。

6. 综合示例代码

cpp 复制代码
#include <utility>
#include <string>
using namespace std;

int val = 10;
int& getL() { return val; }
int&& getX() { return move(val); }

int main() {
    // 左值
    val;
    getL();
    const int c = 5;
    c;

    // 纯右值
    123;
    1 + 2;
    string("test");

    // 将亡值(xvalue,右值)
    move(val);
    getX();

    // 赋值测试
    getL() = 999; // OK 左值可赋值
    // 123 = 10; 报错
    // move(val) = 20; 报错,右值不可赋值
    return 0;
}

int g = 100;

int& getG() { return g; }

getG() = 200; // 合法,返回左值,可以赋值

getG() = 200; 这一步会得到什么结果

运行结果分步拆解

cpp 复制代码
int g = 100;
int& getG() { return g; }
getG() = 200;

1. 核心原理

getG() 返回 int& 左值引用 ,等价于直接返回变量 g 本身的别名。

getG() 就等同于 g

2. 执行过程

  1. 调用 getG(),函数内部 return g,返回 g 的左值引用;
  2. 赋值语句 getG() = 200 等价于 g = 200
  3. 全局变量 g 的值被修改。

3. 最终结果

变量 g 的值从 100 变成 200

完整验证代码

cpp 复制代码
#include <iostream>
using namespace std;

int g = 100;
int& getG() {
    return g;
}

int main() {
    cout << g << endl;   // 输出 100
    getG() = 200;
    cout << g << endl;   // 输出 200
    return 0;
}

延伸对比(反例:返回普通int)

如果函数返回值是 int(纯右值),就不能赋值:

cpp 复制代码
int getG2() { return g; }
getG2() = 300; // 编译报错,纯右值不能放在赋值左侧

二、面试常问问题

C++ 完整实现代码(满足全部需求)

需求梳理

  1. 分配函数:通过参数带出堆内存首地址(C++ 可用指针引用 unsigned char*&,比二级指针更简洁)
  2. 写入函数:填充 0x00、0x11、0x22 ... 0x99 共10字节
  3. 打印函数:控制台输出所有字节十六进制
  4. 堆内存使用完毕释放,防止内存泄漏
cpp 复制代码
#include <iostream>
// new/delete 无需额外头文件,iostream已包含基础内存操作

// 1. 内存分配函数:使用指针引用带出堆内存首地址
void allocMemory(unsigned char*& buf, int bufSize)
{
    // 在堆区分配 bufSize 字节空间
    buf = new unsigned char[bufSize];
    if (buf == nullptr)
    {
        std::cerr << "堆内存分配失败!" << std::endl;
        exit(1);
    }
}

// 2. 数据写入函数:填充 0x00, 0x11, 0x22 ... 0x99
void writeBuffer(unsigned char* buf, int bufSize)
{
    for (int i = 0; i < bufSize; i++)
    {
        buf[i] = static_cast<unsigned char>(i * 0x11);
    }
}

// 3. 数据输出函数:循环打印每个字节的十六进制
void printBuffer(const unsigned char* buf, int bufSize)
{
    std::cout << "缓冲区字节数据:";
    for (int i = 0; i < bufSize; i++)
    {
        // 按两位十六进制输出,不足补0
        printf("0x%02X ", buf[i]);
    }
    std::cout << std::endl;
}

int main()
{
    const int BUF_LEN = 10;
    unsigned char* pBuffer = nullptr;

    // 分配10字节堆内存,pBuffer通过函数参数被赋值
    allocMemory(pBuffer, BUF_LEN);

    // 写入指定数据
    writeBuffer(pBuffer, BUF_LEN);

    // 屏幕输出
    printBuffer(pBuffer, BUF_LEN);

    // 释放堆内存
    delete[] pBuffer;
    pBuffer = nullptr;

    return 0;
}

运行输出结果

复制代码
缓冲区字节数据:0x00 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99

关键知识点说明

1. 分配函数 unsigned char*& buf

  • unsigned char*&指针的左值引用 ,相当于C语言二级指针 unsigned char**,但C++语法更优雅
  • 函数内修改 buf 会直接改变外部实参 pBuffer,实现「通过参数带回首地址」要求

2. 堆内存分配与释放

  • C++ 使用 new[] 分配数组堆内存,对应释放必须用 delete[]
  • 分配后主动置空 nullptr,避免野指针

3. 数据填充逻辑

buf[i] = i * 0x11

i=0 → 0x00,i=1→0x11 ... i=9→0x99,完美匹配题目要求

4. 输出格式化

%02X:保证十六进制固定两位,例如数值 0x05 会打印 0x05 而非 0x5,格式统一


兼容C风格二级指针版本(可选,如果你习惯C写法)

cpp 复制代码
void allocMemory(unsigned char** buf, int bufSize)
{
    *buf = new unsigned char[bufSize];
    if (*buf == nullptr)
    {
        std::cerr << "分配失败" << std::endl;
        exit(1);
    }
}
// main中调用:allocMemory(&pBuffer, BUF_LEN);
cpp 复制代码
#include <iostream>
#include "as.h"

using namespace std;

int main(int argc, const char *argv[])
{
	int result;

	result = add(10,20);//addii
	cout << "add(10,20) =  " << result << endl;

	result = sub(10,20);//subii
	cout << "sub(10,20) =  " << result << endl;
	
	return 0;
}

任务完整实现(Linux GCC)

任务目标

  1. 用C语言编写动态库,自定义实现strcpystrcat
  2. C++代码调用该C动态库函数(必须使用extern "C"解决C/C++符号兼容)
  3. 分三步:库源码→编译动态库→C++测试程序

一、C动态库源码

1. 头文件 mystring.h

c 复制代码
#ifndef MYSTRING_H
#define MYSTRING_H

// 自定义strcpy:字符串拷贝
char* my_strcpy(char* dest, const char* src);
// 自定义strcat:字符串拼接
char* my_strcat(char* dest, const char* src);

#endif

2. 库实现文件 mystring.c

c 复制代码
#include "mystring.h"

char* my_strcpy(char* dest, const char* src)
{
    char* p = dest;
    while (*src != '\0')
    {
        *p = *src;
        p++;
        src++;
    }
    *p = '\0'; // 追加字符串结束符
    return dest;
}

char* my_strcat(char* dest, const char* src)
{
    char* p = dest;
    // 先走到dest字符串末尾
    while (*p != '\0')
        p++;
    // 再拷贝src到dest尾部
    while (*src != '\0')
    {
        *p = *src;
        p++;
        src++;
    }
    *p = '\0';
    return dest;
}

二、编译生成Linux动态库(libmystring.so

执行终端命令:

bash 复制代码
# 1. 生成位置无关目标文件
gcc -c -fPIC mystring.c -o mystring.o
# 2. 链接生成动态共享库
gcc -shared mystring.o -o libmystring.so

输出文件:libmystring.so(Linux标准动态库)


三、C++测试程序 main.cpp

核心要点:extern "C" 包裹C库函数声明,关闭C++名字改编,保证能找到C库符号

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

// 关键:extern "C" 让C++以C符号规则链接动态库函数
extern "C"
{
    char* my_strcpy(char* dest, const char* src);
    char* my_strcat(char* dest, const char* src);
}

int main()
{
    char buf[128] = {0};
    // 测试strcpy
    my_strcpy(buf, "Hello ");
    cout << "拷贝结果:" << buf << endl;

    // 测试strcat拼接
    my_strcat(buf, "Dynamic Library");
    cout << "拼接结果:" << buf << endl;

    return 0;
}

编译C++程序并链接动态库

bash 复制代码
# 编译:指定动态库路径 -L. ,链接库 -lmystring
g++ main.cpp -o test_demo -L. -lmystring

运行程序(临时加载当前目录动态库)

bash 复制代码
# Linux需要指定动态库搜索路径
export LD_LIBRARY_PATH=.
./test_demo

运行输出

复制代码
拷贝结果:Hello 
拼接结果:Hello Dynamic Library

四、关键知识点说明 💡

  1. -fPIC:生成位置无关代码,动态库必须加该参数,支持内存地址重定向
  2. -shared:GCC生成共享动态库的参数
  3. extern "C"
    • C++默认会对函数名做名字改编(name mangling),C动态库是纯C符号,二者不匹配会报undefined reference
    • extern "C"包裹声明,强制使用C风格符号名,实现C++调用C库
  4. 动态库链接参数
    • -L.:告诉编译器在当前目录寻找库文件
    • -lmystring:链接libmystring.solib前缀与.so后缀自动省略
  5. LD_LIBRARY_PATH :Linux运行时动态库搜索环境变量,程序启动时去该路径加载.so

Windows平台补充(MinGW编译)

生成dll动态库命令

bash 复制代码
gcc -c -fPIC mystring.c
gcc -shared mystring.o -o libmystring.dll

C++编译运行

bash 复制代码
g++ main.cpp -o test.exe -L. -lmystring
# 运行时将libmystring.dll和test.exe放同一目录
./test.exe