《深入理解计算机系统》读书笔记(ch2)+ C 泛型

本章主要介绍各类型的机器表示,Stanford的CS107的lec2和lec3有精彩解释,比看书快(当作书中只是的cache吧)。

lec4中介绍的C里面如何使用泛型(没有template, reference)的技巧在此记录一下:

/*普通的swap函数*/
void
swap(int *ap, int *bp) { int temp = *ap; *ap = *bp;*bp = temp; } int x = 7; int y = 117; swap(&x, &y);//x=117, y=7

/*泛型swap函数, 错误实现*/
void swap(void *vp1, void *vp2)
{
void temp = *vp1; //不能dereference void指针,因为不知道指针类型,也就是不知道何时停止
*vp1 = *vp2;
*vp2 = temp;
}


/*泛型swap函数,正确实现*/
void swap(void *vp1, void *vp2, int size)//关键是交换bit pattern
{
char buffer(size);//需要编译器支持入参定义数组大小,char类型不重要,只是复制bit pattern。编译器不支持换用malloc即可
memcpy(buffer,vp1,size);
memcpy(vp1,vp2,size);
memcpy(vp2,buffer,size);
}
//对普通变量使用swap
int x=7,y=117;
swap(&x,&y,sizeof(int));
//对指针变量使用swap
char *husband = strdup("Fred");
char *wife = strdup("Wilma");
swap(&husband, &wife, sizeof(char *));

对比template实现的优势:不会导致代码膨胀(不同类型的template会在二进制中复制自己的版本)。

劣势:内存管理复杂。

先介绍一下泛型实现的linear search:

//linear search for int array
int lsearch(int key, int array[], int size)
{
    for (int i=0 ;i<size; i++)
    {
        if( array[i] == key){
            return i;    
        }
    }
}        


//generic linear search,只对基本类型有效,struct等可包含指针的类型无效
void  *lsearch( void *key, void *base, int n, int elemSize)
{
    for(int i=0; i<n; i++)
    {
        void *elemAddr = (char *)base + i*elemSize;//void star hack,将指针算数等价为普通算数运算,因为char的大小为1
        if(memcmp(key,elemAddr,elelSize)==0){
            return elemAddr;
        }
    }
}



//generic linear search
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;//void star hack,将指针算数等价为普通算数运算,因为char的大小为1
        if(cmpFn(key, elemAddr) == 0){//hook
return elemAddr;
}
}  
return NULL; }

//use lsearch for int
int array[] = {4,2,3,7,11,6};
int size = 6;
int number = 7;

int IntCmp(void *elem1, void *elem2)
{
int *ip1 = elem1;
int *ip2 = elem2;
return *ip1-*ip2;
}

int *found = lsearch(&number,array,6,sizeof(int),IntCmp);
if(found==NULL)printf("not found");

//use lsearch for char*
char *notes[] = {"Ab","F#","B","Gb","D"};
char *favoriteNote = "Eb";

int StrCmp(void *elem1, void *elem2)
{
char *s1 = *(char **)elem1;// 先说明指针类型,然后解引用,得到一个char *
char *s2 = *(char **)elem2;
return strcmp(s1,s2);
}

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

//实际上lsearch和bsearch都是内建泛型函数,bsearch接口如下
void *bsearch(void *key, void *base, int n, int elemSize, int (*cmp)(void *, void *));

 C中同样没有类的概念,结构体中的变量只能是public,要实现一个类似Stack的数据结构需要程序员自己注意用法(不要直接使用struc中的数据成员,而是使用配套的“方法”)

typedef struct{
    int *elems;
    int logicalLen;
    int allocLen;
}Stack;//12 bytes

void StackNew(Stack *s){
    s->logicalLen = 0;
s->allocLen = 4;
s->elems = malloc(4*sizeof(int));
assert(s->elems != NULL); }
void StackDispose(Stack *s){
free(s->elems);//be careful, s->elems shouldn't be null
}
void StackPush(Stack *s, int value){
if(s->logicalLen == s->allocLen){//空间不足时分配双倍空间
s->allocLen *=2;
s->elems = realloc(s->elems,s->allocLen*sizeof(int));//记得捕捉返回值,若地址变动可能指向死内存
//fun fact: C++ doesn't have its own realloc
assert(s->elems!=NULL);
}
s->elems[s->logicalLen] = value;
s->logicalLen++;
}
void StackPop(Stack *s){
assert(s->logicalLen>0);
s->logicalLen--;
return s->elems[s->logicalLen];
}
//使用Stack Stack s;//分配内存 StackNew(&s); for(int i = 0; i<5; i++){ StackPush(&s, i); } StackDispose(&s);

 下面介绍泛型实现的

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef struct{    
void* elems;    
int logicalLen;

int allocLen; int elemSize; void (*freeFn)(void*); }Stack; void StackNew(Stack *s, int elemSize, void (*freeFn)(void*)){ assert(s->elemSize>0); s->elemSize = elemSize; s->logicalLen = 0; s->allocLen = 4; s->elems = malloc(4*elemSize);
s->freeFn = freeFn; assert(s
->elems != NULL); } void StackDispose(Stack *s){
if(s->freeFn!=NULL){
for(int i=0;i<s->logicalLen;i++){
s->freeFn((char*)s->elems + i*s->elemSize);
}
}
free(s->elems); } static void StackGrow(Stack *s){//static声明的函数只能在本文件中被使用 s->allocLen *=2; s->elems = realloc(s->elems, s->allocLen*s->elemSize ); } void StackPush(Stack *s, void *elemAddr){ if(s->logicalLen == s->allocLen) StackGrow(s); void *target = (char*)elems + s->logicalLen*s->elemSize; memcpy(target,elemAddr,s->elemSize); s->logicalLen++; } void StackPop(Stack *s, void *elemAddr){ void *source = (char*)s->elems + (s->logicalLen-1)*s->elemSize; memcpy(elemAddr, source, s->elemSize); s->logicalLen--; }


//Usage
void StringFree(void *elem){
free(*(char **)elem);//a bit complicated,careful here, 每个*看作内存中一跳,想清楚目标是哪里
}
int main(){
const char *names[] = {"Al","Bob","Carls"};
Stack stringStack;
StackNew(&stringStack,sizeof(char*),&StringFree);
for(int i=0; i<3; i++){
char *copy = strdup(names[i]);
StackPush(&stringStack,&copy);
}
char *name;
for(int i=0; i<3; i++){
StackPop(&stringStack, &name);
printf("%s/n",name);
free(name);
}
StackDispose(&stringStack);
}

 

posted @ 2018-10-27 00:19  42&Curry  阅读(140)  评论(0编辑  收藏  举报