编程范式(斯坦福大学)学习笔记《五》

线性搜索

void *lsearch(void* key,void base,int n,int elemSize, int (*cmpfn)(void *,void *)){
    for(int i<0;i<n;i++) {
        void* elemAddr=(char*)base+i*elemSize;
        //这里的强制转换成char*只是为了让编译器可以进行运算,找到每一次比较的起始地址elemaddr
        if(cmpfn(key,elemAddr)==0)//callback
            return elemAddr;
    }
    return NULL;
}  

代码第三行:为什么要对base指针进行强制类型转换,转换成char呢?这跟上一篇文章中的讲到的原因一样,对于int类型的指针来说,我们知道每次进行++操作,指针会自增4字节,然而对于void类型指针进行加法操作,编译器不知道每次要加多少。所以我们将base指针强制转换成char,这样编译器就知道指针加法的基数是1。


int array[]={4,2,3,7,11,6};
int size=6;
int number=7;
int* found=lsearch(&member,array,6,sizeof(int),intCmp);
if(found==NULL){...}
else{...}

 

下面我们来看cmpFn的实现,假如我们要查找的类型是int,则:

int intCmp(void* vp1,void* vp2){
    int* ip1 = vp1;
    int* ip2 = vp2;
    return *ip1 - *ip2;
}

 

不过必须认识到:这种写法并不是支持泛型的最简明的方式,他只是C语言及其规范中能用的最好的方式,但这种方式执行速度更快,而且只需要一份代码副本来完成所有的线性搜索。

 

OK,假如现在我们有一个字符串数组:

char *notes={"Ab","F#","B","Gb","D"};

利用上面的lSearch代码,我们要查找"Eb",则调用形式应该是: 

char *favoriteNote="Eb";
char **found = lsearch(&favoriteNote,notes,5,sizeof(char *),StrCmp);

其中strCmp是我们要自己提供的。注意lsearch的的第一个参数是一个void类型,而favoriteNote是char类型,这里我们传递给它的则是favoriteNote的取地址,即一个(char**)类型的变量,并且elemSize取的是char类型指针的大小。我们先实现strCmp,然后再来说为什么。

int StrCmp(void* ip1,void* ip2){
    char* s1=*(char **)vp1;
    /*
    char *s1=(char *)vp1;
    对应lsearch(favoriteNote,notes,5,sizeof(char *),StrCmp)
    */
    char* s2=*(char **)vp2;
    return strcmp(s1,s2);
}

这里我们将两个void指针强制转换成char**类型,然后再进行解引用。这样做的意义在于,针对字符串数组来说,字符串是存储在常量区域的,字符串类型的变量是一个指针,而字符串数组存储的就是这些指针。对于notes[1]来说,它存储的是一个地址,这个地址指向的内存空间存储的是"F#"。假如我们直接写成:

char *s1=(char *)vp1;

那么,StrCmp比较的则是存储字符串的地址,而不是字符串本身。这也是为什么lsearch的第一个参数,我们传递的是favoriteNote的取地址,而不是直接的favoriteNote。

 

stack的纯C语言实现(只针对于int型):

//stack.h
typedef struct{
int *elems;//保存元素的地址
int logicalLen;//已存在的个数
int allocLen;//可用的内存空间所分配的个数
}stack;
void StackNew(stack *s);         
void StackDispose(stack *s);    
void StackPush(stack *s, int value);       
int StackPop(stack *s);         
//main.c                       
stack s;//分配12bytes的空间            
StackNew(&s);//初始化栈的成员   
for(int i=0;i<5;i++){          
StackPush(&s, i);//进栈     
}                              
StackDispose(&s);//删除栈      


//stack.c
void StackNew(stack *s)
{
s->logicalLen=0;
s->allocLen=4;
s->elems=malloc(4*sizeof(int));
assert(s->element != NULL)//确保成功空间分配
}
void SatckDispose()
{
free(s->elems);
}

猜你喜欢

转载自blog.csdn.net/dakin_/article/details/81003551