跳到主要内容

32 篇博文 含有标签「码农札记」

查看所有标签

.NET Framework 的容器

· 阅读需 5 分钟

自从我发现了 STL(Standard Template Library),它就成了我编写 C++ 程序时的标配。STL 提供的容器和算法极大地简化了开发工作,提高了代码复用性和可维护性。
最近,我开始研究 C# 中的泛型和标准容器,试图了解它们在实现 STL 类似功能时的表现。本篇文章总结了 C# 的标准容器,以及它们的特点和用法。研究的环境是 Visual C# 2005。

C# 的容器分类

在 C# 中,容器大致可以分为两类:数组(Array)集合(Collection)

  1. Array 类
    System.Array 类是 C# 中提供的一种基本容器,可以看作是 STL 中 vector 的类似实现。它提供了数组操作的基础功能,如查找和排序。
    特点

    • 不需要通过专门的类声明变量。直接使用元素类型加中括号即可声明数组,例如:
      int[] myIntArray = new int[5] { 1, 2, 3, 4, 5 };
    • 声明方式直观,但灵活性稍逊于泛型集合。
  2. 集合(Collections)
    集合类在 C# 中主要分布在以下两个命名空间中:

    • System.Collections(C# 1.0)
      该命名空间包含了一些非泛型集合类,如:

      • ArrayList
      • BitArray
      • Hashtable
      • Queue
      • SortedList
      • Stack
        这些容器为早期的 C# 提供了数据存储和操作的基本工具,但由于它们不支持类型安全,使用时需要格外注意类型转换问题。
    • System.Collections.Generic(C# 2.0 引入)
      C# 2.0 引入泛型编程后,该命名空间新增了泛型集合类,极大地提升了代码的类型安全性和性能。主要容器包括:

      • Dictionary:键值对集合,类似于 STL 中的 mapmultimap
      • LinkedList:双向链表实现,用于插入和删除频繁的场景。
      • List:类似于 STL 中的 vector,是一个动态数组。
      • Queue:先进先出(FIFO)集合,与 STL 中的 queue 功能类似。
      • SortedDictionarySortedList:提供有序存储功能。
      • Stack:后进先出(LIFO)集合,与 STL 中的 stack 类似。

与 STL 的对比

C# 和 C++ STL 提供的容器种类有一定相似性,但也存在一些显著差异:

C# 容器STL 容器功能描述
Arrayvector动态数组,支持随机访问。
Dictionarymap/multimap键值对集合,支持快速查找。
List / SortedListlist单链表/有序列表。
Queuequeue先进先出的队列结构。
Stackstack后进先出的栈结构。
BitArraybitset位操作集合。

虽然两者在功能上有很多重叠,但 C# 的集合通过泛型和命名空间分类,实现了更好的类型安全性和模块化。

主要容器简介

  1. Array Array 提供基础数组功能。声明简单,功能全面,例如:

    int[] numbers = new int[] { 1, 2, 3, 4, 5 };
    Array.Sort(numbers);
    Array.Reverse(numbers);

    它类似于 STL 的 vector,但无法动态扩展长度。

  2. Dictionary Dictionary 是一种键值对集合,常用于快速查找和映射。例如:

    Dictionary<string, int> nameToAge = new Dictionary<string, int>();
    nameToAge.Add("Alice", 30);
    nameToAge.Add("Bob", 25);
  3. List List 是一个动态数组,支持快速随机访问和灵活的扩展。例如:

    List<int> numbers = new List<int> { 1, 2, 3 };
    numbers.Add(4);
    numbers.RemoveAt(2);
  4. Queue 先进先出的集合结构:

    Queue<string> tasks = new Queue<string>();
    tasks.Enqueue("Task 1");
    tasks.Enqueue("Task 2");
    string nextTask = tasks.Dequeue();
  5. Stack 后进先出的集合结构:

    Stack<string> history = new Stack<string>();
    history.Push("Page 1");
    history.Push("Page 2");
    string lastVisited = history.Pop();

总结

C# 提供的容器丰富多样,尤其是泛型容器在性能和类型安全性上远胜于 C# 1.0 中的非泛型集合。虽然它们与 STL 的容器设计思路有所不同,但在功能实现上,C# 集合为开发者提供了相当强大的工具。无论是选择 Array 还是泛型集合,开发者都能根据需求灵活使用。

查看或添加留言

DLL的导入库中是什么信息

· 阅读需 3 分钟

当我们在项目中使用DLL时,链接器需要查找DLL输出的变量、函数或C++类等符号信息,并自动生成一个对应的.lib文件。这个.lib文件被称为导入库,它包含了DLL输出的符号列表。如果一个可执行模块需要引用DLL中的符号(使用GetProcAddress除外),则必须链接该导入库。

导入库中包含的信息

实际上,导入库中并不包含符号的相对虚拟地址(RVA)。它仅仅是一些符号以及与其相关的元数据,例如该库对应的DLL的名称等。(这是我当前的理解,欢迎探讨)。

应用程序如何调用DLL中的函数

当一个应用程序通过导入库调用DLL中的函数时,这个过程是在进程的主线程开始运行之前由加载器完成的。

加载器按照可执行文件的输入节中指定的DLL名称,通过Windows的搜索路径找到并加载对应的DLL。加载成功后,DLL被映射到进程的地址空间中,此时,加载器根据DLL的实际加载地址和符号信息,将每个输入节中的符号地址填入可执行模块的输入节中,完成动态链接。

这一过程解释了为什么可以使用lib命令通过.def文件生成导入库。

使用def文件生成lib文件的示例

以下是一个完整的操作流程:

1. 创建一个.def文件

LIBRARY
xujian.dll
EXPORTS
xujian

2. 使用lib命令生成.lib文件

运行以下命令生成导入库:

lib /def:XX.def /machine:i386 /out:XX.lib

3. 编写一个测试程序

在工程文件中加入生成的XX.lib后,编写如下代码:

extern "C" void xujian();

int main()
{
xujian();
return 0;
}

编译和链接后,程序应该可以正常运行。

查看或添加留言