巧用bsearch--stanford CS107 assignment 2 总结

本文探讨了使用bsearch函数解决复杂内存查找问题的技巧,并通过实例展示了如何高效地进行内存操作,特别关注了如何在bsearch函数中利用基址进行复杂查找,以及在编程实践中的应用。

    作为一个已经有CS硕士文凭的毕业生,回头来听别人牛逼学校大学基础CS课程,还听得津津有味,不得不说,很惭愧啊,看的过程总是在想,如果刚进大学的时候就接触到如此视频该有多好,假如国内哪个老师哪怕能原本照搬别人的讲课内容该有多好,不过话说回来,以当时的情况估计要先修个两三年英语才够水平哈。光听stanford这些大学的公开课,已经觉得自己以前上的课是一坨屎了,我以为差距也到此为止了,直到我尝试做了一下他们的assignment,才发现,什么是教学质量啊,一份作业光描述就快20页了,作业从来都不是玩具代码,本身就是一个很有意思的程序,然后定义了一些接口作为作业给学生实现,那个叫编码规范啊,尼玛,注释详细得都快可以写成文章了。代码漂亮也就算了,代码本身那个叫好玩啊,又是句子自动生成机,又是IMDB演员配对,我他妈做这些题就跟乡巴佬进城似的,还好原本就是IMDB评分狂热爱好者,不然演员都不知道啊。

    programming paradigms的assignment 2很有意思,满足了我对电影和编程学习的双重爱好,可惜的是,它需要unix环境,在windows中用minGWf编译都不成,很是郁闷,难道硬要我装个linux不成?所以我只能小小尝试,却不能享受最后程序运行的快感。

编程过程中碰到个难题,就是它有个void* actorFile指向一长串raw data空间,结构的话,我贴个作业中截出来的图:


    第一个int,代表总共的演员数,然后是每个演员的int索引,int的大小代表它到基址的偏移字节。每个演员的索引之后,便是演员的信息,名字在最前面。

    作业有一个地方需要查找一个演员是否在actorFile中,若在的话把演员的相关信息提取出来。这个actorFile空间是超大的,这个例子是简化的。

    它推荐用bsearch,但这可难倒我的,把string作为key传入,但bearch查找的区域是int区啊,int值本身是偏移,要获取相应的演员信息还需要基址,可bsearch是这么个函数:

    void *bsearch(const void *key, const void *base, size_t *nelem, size_t width, int(*fcmp)(const void *, const void*));

    cmp如何才能利用到base呢?后来苦思不得,看了assignment 2的FAQ,有一个问题和回答是这样的:

  Question: If I define a top-level comparison function, I don't have access to actorFile or movieFile. How can I translate integer 
offsets into movie and actor locations if I don't have access to either of those variables.  
  Answer: Excellent question. The programmer can pass the address of anything s/he wants as the first argument of bsearch. 
You're probably passing the address of a string or a film, but it would make more sense to pass the address of a struct as the key. 
You can include anything you think your comparison function will need to do a full comparison of actors and movies. This requires 
a genuine understanding of how function pointers and casting works, but it's the genuine understanding that we're shooting for.  

    直接命中G点,基本上就是我要提的问题啊,key不能传string,而是个结构体,在我以为要豁然开朗的时候,却笨笨地还是想不到何解。其实关键是不知道bsearch是如何运作的,大部分网上对bsearch的描述都不够细,直到搜到微软自己的MSDN,它对fcmp这个比较函数的两个参数有具体描述,前者传入的是key,后者传入的是搜索区的相应指针,这才算是明白了,通过key来传入基址,然后在fcmp中进行解析。我靠,终于TM有解了。

   以下是我写的测试代码,没办法,只有模拟actorFile了。对于这种bsearch用法,感觉非常巧妙,第一次如此自如地操作内存,发现所谓的数据结构完全可以不用struct或class来抽象,直接void*,自己在内存中操作就行了,不过调试起来不方便就是了,要通过void*指向的地址来看内存的分布。

  还有几个需要注意的地方,比较函数中*(void**)代表的是对指向actorFile这个void*的指针的dereference,还有就是名字的排序需要按字母排序,因为bsearch的前提是有序,这里体现在名字字符串中,而不是所谓的int偏移区,假如名字不按字母排序,将找不到想要的名字,有兴趣的同学也可以试下调换名字位置。

int compFunc(const void* key, const void* b)
{
  return strcmp((char*)((int*)key+1) , (char*)*(void**)key+*(int*)b);
}

int main()
{
  void* actorFile=malloc(4*sizeof(int)+4+4+6);
  int num=3;
  int first=16;
  int second=20;
  int third=24;
  char* s1="Bob";
  char* s2="Ted";
  char* s3="Venus";
  memcpy(actorFile,&num,sizeof(int));
  memcpy((int*)actorFile+1,&first,sizeof(int));
  memcpy((int*)actorFile+2,&second,sizeof(int));
  memcpy((int*)actorFile+3,&third,sizeof(int));
  memcpy((int*)actorFile+4,s1,4);
  memcpy((int*)actorFile+5,s2,4);
  memcpy((int*)actorFile+6,s3,6);

  int numOfActor=*(int*)actorFile;
  void* key=malloc(sizeof(int)+5);
  memcpy(key,&actorFile,4);
  memcpy((int*)key+1,s3,6);
  void *item=bsearch(key, (int*)actorFile+1, numOfActor, sizeof(int), compFunc);
  if(item==NULL)
	  cout<<"can not find string"<<endl;
  else
  {
	char* findStr=(char*)actorFile+*(int*)item;
	cout<<findStr<<endl;
  }
}


    


<think> 首先,用户的问题是关于bsearch的使用方法和功能介绍。我需要基于提供的引用内容来回答,这些引用来自站内引用[1]到[5]。 关键点从引用中提取: - 引用[1]:bsearch函数原型是 `void *bsearch(const void *key, const void *base, size_t nelem, size_t width, int(*fcmp)(const void *, const *));` 它用于二分查找。[^1] - 引用[2]:bsearch包含在<stdlib.h>头文件中。它实现二分查找,如果找到元素则返回指向该元素的指针,否则返回NULL。对于多个匹配元素,行为未定义。需要自定义比较函数。原型是 `void *bsearch(const void *key, const void *base, size_t num, size_t size, int (*cmp)(const void *, const void *));`[^2] - 引用[3]:函数原型 `void* bsearch (const void* key, const void* base, size_t num, size_t size, int (*compar)(const void*,const void*));`[^3] - 引用[4]:C库函数 `void *bsearch(const void *key, const void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *))` 对nitems对象的数组执行二分查找。base指向数组,key指向要查找的元素,size指定元素大小。数组必须按compar比较函数升序排序。[^4] - 引用[5]:示例代码使用bsearch,如 `bsearch(szStuID, pStuTable, NUM, sizeof(STU_TAB), (int(*)(const void *,const void *))CompareFunc);`[^5] 综合这些,bsearch是C语言中的一个函数,用于在排序后的数组中执行二分查找。它需要用户提供比较函数来定义元素的比较方式。 现在,用户的问题是:“我想查找关于bsearch的相关信息,可能包括使用方法、功能介绍等 请问bsearch的使用方法和功能介绍” 我需要构建一个清晰、结构化的回答。根据系统指令: - 使用中文回答。 - 行内数学表达式用$...$,但这里可能不需要数学表达式。 - 独立公式用$$...$$,但bsearch的函数原型可以用它。 - LaTeX语法正确。 - 生成相关问题。 - 在回答中引用段落末尾添加引用标识,如[^1]。 - 回答结构清晰,逐步解决问题。 - 保证回答真实可靠,基于引用。 结构回答: 1. **功能介绍**:简要介绍bsearch是什么,基于引用。 2. **函数原型**:列出函数原型,使用独立公式格式。 3. **使用方法**:解释如何使用,包括参数含义和比较函数。 4. **注意事项**:如数组必须排序,返回指针等。 5. **示例**:提供一个简单示例,基于引用[5]。 最后,生成相关问题。 回答大纲: - 开头:介绍bsearch- 主体: - 函数原型:使用$$...$$格式。 - 参数解释:key, base, num, size, compar。 - 比较函数:如何定义。 - 返回值:找到则返回指针,否则NULL。 - 数组要求:必须升序排序。 - 使用步骤:如何调用。 - 示例代码:从引用[5]改编一个简单示例。 - 结尾:总结。 生成相关问题:例如关于性能、优化、错误处理等。 添加引用:在相关句子末尾添加[^n]。 确保语言中文。 </think>### bsearch 函数介绍和使用方法 bsearch 是 C 语言标准库中的一个函数,用于在已排序的数组中执行高效的二分查找(Binary Search)。它包含在 `<stdlib.h>` 头文件中,可以快速定位指定元素的位置。二分查找是一种高效的搜索算法,时间复杂度为 $O(\log n)$,适用于大型有序数组[^2][^4]。下面我将逐步介绍其功能和使用方法,确保内容基于可靠的引用来源。 #### 1. **功能介绍** - **核心功能**:bsearch 在有序数组中进行二分查找。给定一个关键字(key),它会返回指向该元素在数组中的指针(如果找到),否则返回 NULL。这避免了手动实现二分查找的复杂性[^2][^4]。 - **优点**:查找速度快,特别适合静态或只读数据。二分查找通过每次比较将搜索范围减半,确保高效性(时间复杂度为 $O(\log n)$)[^2]。 - **限制**: - 数组必须按升序排序(基于比较函数),否则行为未定义。 - 如果多个元素匹配关键字,bsearch 未指定返回哪一个(可能返回任意匹配元素)[^2][^4]。 - 需要用户自定义比较函数,以支持不同数据类型的比较逻辑。 #### 2. **函数原型** bsearch 的函数原型如下(使用独立公式格式): ```c void *bsearch(const void *key, const void *base, size_t num, size_t size, int (*compar)(const void *, const void *)); ``` 参数说明: - **key**:指向要查找的关键字(元素值)的指针。类型为 `const void *`,表示可以接受任意类型的数据地址,例如整数、结构体等[^2][^4]。 - **base**:指向数组首元素的指针。数组必须连续存储且已排序[^4]。 - **num**:数组中的元素数量,类型为 `size_t`(无符号整数)[^3][^4]。 - **size**:每个元素的大小(字节数),类型为 `size_t`。例如,如果数组元素是整数,size 为 `sizeof(int)`[^4]。 - **compar**:指向比较函数的指针。该函数由用户定义,用于比较两个元素。原型为 `int compar(const void *a, const void *b)`: - 如果 `a < b`,返回负整数(通常为 -1)。 - 如果 `a == b`,返回 0。 - 如果 `a > b`,返回正整数(通常为 1)[^2][^4]。 返回值: - 如果找到匹配元素,返回指向该元素的指针(类型为 `void *`)。 - 如果未找到,返回 NULL[^2][^4]。 #### 3. **使用方法步骤** 以下是使用 bsearch 的详细步骤,确保安全可靠: 1. **包含头文件**:在 C 代码开头添加 `<stdlib.h>`。 2. **定义比较函数**:根据元素类型编写比较函数。例如,对于整数数组: ```c int compare_int(const void *a, const void *b) { int val1 = *(const int *)a; // 转换为实际类型 int val2 = *(const int *)b; if (val1 < val2) return -1; if (val1 > val2) return 1; return 0; } ``` 对于结构体数组(如引用[5]中的学生记录),需要自定义比较逻辑: ```c typedef struct { char id[10]; // 示例结构体 int score; } STU_TAB; int compare_stu(const void *a, const void *b) { const STU_TAB *stuA = (const STU_TAB *)a; const STU_TAB *stuB = (const STU_TAB *)b; return strcmp(stuA->id, stuB->id); // 比较学生ID } ``` 3. **准备数组**:确保数组已按升序排序(例如使用 qsort 函数)。 4. **调用 bsearch**: - 传递关键字指针、数组指针、元素数量、元素大小和比较函数。 - 检查返回值是否为 NULL 判断查找结果。 #### 4. **注意事项** - **数组排序**:bsearch 要求数组严格升序排序(基于 compar 函数),否则结果未定义。建议先用 qsort 排序数组[^4]。 - **内存安全**:确保 key 和 base 指向有效内存,避免空指针错误。 - **多元素匹配**:如果有重复元素,bsearch 可能返回任意匹配项,这不是错误,但需在代码中处理[^2]。 - **类型安全**:比较函数中需正确转换 void 指针类型,防止类型错误[^5]。 #### 5. **简单示例** 以下是一个完整示例,基于引用[5]中的学生记录查找进行改编: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char id[10]; // 学生ID char name[20]; } STU_TAB; // 比较函数:按学生ID排序 int compare_stu(const void *a, const void *b) { const STU_TAB *stuA = (const STU_TAB *)a; const STU_TAB *stuB = (const STU_TAB *)b; return strcmp(stuA->id, stuB->id); // 升序比较 } int main() { STU_TAB students[] = { {"1001", "Alice"}, {"1002", "Bob"}, {"1003", "Charlie"} }; size_t num = sizeof(students) / sizeof(students[0]); // 元素数量 // 先排序数组(bsearch要求) qsort(students, num, sizeof(STU_TAB), compare_stu); // 查找学生ID为"1002"的记录 char key_id[] = "1002"; STU_TAB *result = (STU_TAB *)bsearch( key_id, // 关键字指针 students, // 数组基址 num, // 元素数量 sizeof(STU_TAB), // 元素大小 compare_stu // 比较函数 ); if (result != NULL) { printf("找到学生: ID=%s, 姓名=%s\n", result->id, result->name); } else { printf("未找到该学生记录\n"); } return 0; } ``` 在这个示例中: - 数组 `students` 必须先排序(使用 qsort)。 - bsearch 查找关键字 `key_id`,返回指向匹配元素的指针或 NULL。 - 比较函数 `compare_stu` 处理结构体比较[^5]。 ### 总结 bsearch 是 C 语言中高效的二分查找函数,适用于有序数组查询。它简化了搜索实现,但要求数组排序并提供自定义比较函数。使用时注意内存和类型安全,以避免常见错误。如果您有特定场景(如自定义数据类型),我可以提供更针对性的建议。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值