資料來源:南科大 余仕琪 C/C++ Program Design
LINK:CPP/week06 at main · ShiqiYu/CPP · GitHub
一、本節內容
? ? ? ? 本節主要介紹靜態庫和動態庫。
1.1 靜態庫和動態庫的概念

????????靜態鏈接和靜態庫(也稱為存檔)是鏈接器將所有使用的庫函數復制到可執行文件的結果。靜態鏈接會創建更大的二進制文件,并且需要更多的磁盤和主存空間。靜態庫的示例包括Linux中的.a文件和Windows中的.lib文件。
????????動態鏈接和動態庫動態鏈接不需要復制代碼,只需將庫的名稱放在二進制文件中即可。實際的鏈接發生在程序運行時,當二進制文件和庫都在內存中時。如果系統中的多個程序鏈接到同一動態鏈接庫,則它們都引用該庫。因此,該庫由多個程序共享,稱為“共享庫”。動態庫的示例包括Linux中的.so和Windows中的.dll。
1.2?靜態庫和動態庫的區別
?
| 優點 | 缺點 | |
| 靜態庫 | 1.使可執行文件具有較少的依賴關系,已打包成可執行文件。 2.鏈接在編譯階段完成,代碼在執行過程中快速加載。 | 1.使可執行文件比那的更大。 2.作為依賴于另一個庫的庫將導致冗余副本,因為它必須與目標文件打包在一起。 3.升級不方便、不容易。需要替換并重新編譯整個可執行文件。 |
| 動態庫 | 1.動態庫可以實現進程間的資源共享,只能有一個庫文件。 2.升級過程簡單,不需要重新編譯。 | 1.運行時加載會降低代碼的執行速度。 2.添加必須伴隨可執行文件的程序依賴項。 |
?1.3 靜態庫的建立方法
? ? ? ? ?假設我們編寫了以下代碼:





二、習題筆記
習題1

存在的問題:使用new卻后續沒有釋放內存(gpt說的)
????????問題出在使用?new int[SIZE]?分配內存的那一行。當使用?new?動態分配內存時,需要在使用完后使用?delete?來釋放這塊內存。然而,在代碼中,沒有相應的?delete?語句來釋放為?pa?分配的內存。
????????為了解決這個問題,使用智能指針可以自動管理內存,避免手動釋放的問題。例如,std::unique_ptr<int> p(new int);?將在作用域結束時自動釋放內存。
#include <iostream>
#include <memory> // Include the <memory> header for std::unique_ptrusing namespace std;#define SIZE 5int sum(const int *pArray, int n)
{int s = 0;for (int i = 0; i < n; i++)s += pArray[i];return s;
}int main()
{// Use std::unique_ptr to manage memoryunique_ptr<int[]> pa(new int[SIZE]{3, 5, 8, 2, 6});int total = sum(pa.get(), SIZE); // Use pa.get() to access the raw pointercout << "sum = " << total << endl;// No need to manually delete pa; it will be automatically cleaned up when it goes out of scopereturn 0;
}
- ?什么情況下應該使用裸指針而不是智能指針?
-
裸指針(原生指針):
- 裸指針是指直接使用?
T*?類型的指針,沒有被智能指針封裝。 - 適用情況:
- 無所有權語義:當你不需要管理資源的所有權時,可以使用裸指針。例如,函數參數傳遞時,如果不涉及資源所有權的轉移,可以使用裸指針或引用。
- 性能要求高:裸指針操作更輕量,不涉及引用計數等開銷,適用于性能敏感的場景。
- 裸指針是指直接使用?
-
智能指針:
- 智能指針是 C++ 提供的 RAII(資源獲取即初始化)機制的一部分,用于管理動態分配的內存。
- 適用情況:
- 資源管理:在資源獲取時,應優先使用智能指針。它們可以自動清理內存,避免內存泄漏。
- 明確所有權:當需要明確資源的所有權轉移時,使用?
std::unique_ptr?或?std::shared_ptr。 - 線程安全:
std::shared_ptr?可以在多線程環境中共享資源。
-
總結:
- 使用裸指針時,要確保不會出現懸空指針、多次釋放等問題。
- 使用智能指針時,可以更安全地管理資源,但要根據具體情況選擇合適的類型。
習題2

仿真結果:
?問題分析:
????????在?create_array?函數中,聲明了一個名為?arr?的整數數組,并在函數內部對其進行賦值。然后,返回了指向這個局部數組的指針?arr。問題在于,局部數組?arr?是在棧上分配的,而指針?ptr?在?main?函數中持有這個指向局部數組的地址。當?create_array?函數結束時,局部數組?arr?將被銷毀,但指針?ptr?仍然指向已經不存在的內存區域
????????為了避免內存泄漏,我們需要使用動態分配的內存(在堆上分配)來存儲數組。我們可以使用?new?運算符來分配堆內存,并返回指向堆內存的指針。
#include <iostream>
using namespace std;int *create_array(int size)
{int *arr = new int[size]; // 使用 new 分配堆內存for (int i = 0; i < size; i++)arr[i] = i * 10;return arr;
}int main()
{int len = 16;int *ptr = create_array(len);for (int i = 0; i < len; i++)cout << ptr[i] << " ";delete[] ptr; // 釋放堆內存return 0;
}
仿真結果:
![]()
習題3

問題分析:sum函數中常數指針不能被修改賦值
修改方案:將pa改為普通指針
#include <iostream>
#define SIZE 5
void sum( int *, const int *, int);int main()
{int a[SIZE] = {10,20,30,40,50};int b[SIZE] = {1,2,3,4,5};std::cout << "Before calling the function, the contents of a are:" << std::endl;for(int i = 0; i < SIZE; i++)std::cout << a[i] << " ";// passing arrays to functionsum(a,b,SIZE);std::cout << "\nAfter calling the function, the contents of a are:" << std::endl;for(int i = 0; i < SIZE; i++)std::cout << a[i] << " ";std::cout << std::endl;return 0;
}void sum( int *pa, const int *pb, int n)
{for(int i = 0; i < n; i++){*pa += *pb;pa++;pb++;}
}
?
習題4

swap.hpp
#ifndef __SWAP_HPP__
#define __SWAP_HPP__
void swap(int& a, int& b);
#endif
swap.cpp
#include <iostream>
#include "swap.hpp"void swap(int& a, int& b)
{int temp = a;a = b;b = temp;
}
main.cpp
#include <iostream>
#include "swap.hpp"int main()
{int x = 10;int y = 20;std::cout << "Before swapping: x = " << x << ", y = " << y << std::endl;// 調用交換函數swap(x, y);std::cout << "After swapping: x = " << x << ", y = " << y << std::endl;return 0;
}
????????按照1.3節內容進行操作,結果如下所示、將生成的libswap.a庫文件移除之后仍然可以正常運行可執行文件,表面成功建立靜態庫。
? ? ? ?在編寫swap.cpp中使用了引用(參考C++學習日記 | Lecture 6 函數-CSDN博客)而不是傳統的參數作為函數輸入。
使用引用作為函數的輸入
引用可以直接修改原始變量的值:?當我們傳遞參數時,如果使用引用,我們實際上傳遞的是原始變量的引用,而不是它的副本。這意味著在函數內部對引用的修改會直接影響原始變量。如果我們使用值傳遞(
int),則函數內部的修改只會影響參數的副本,而不會影響原始變量。效率更高:?使用引用避免了復制大型對象的開銷。當我們傳遞大型結構體或類對象時,使用引用可以提高性能,因為不需要復制整個對象。
語義更清晰:?使用引用可以更清楚地表達我們的意圖。當我們在函數中看到引用參數時,我們知道這個函數可能會修改原始變量的值。