1. 程式人生 > >《深入理解計算機系統》讀書筆記(ch2)+ C 泛型

《深入理解計算機系統》讀書筆記(ch2)+ C 泛型

tex byte 指向 get 讀書筆記 class its n) 支持

本章主要介紹各類型的機器表示,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);

下面介紹泛型實現的stack

typedef struct{
    void* elems;
    int logicalLen;
    int allocLen;
    int elemSize;

}Stack;


void StackNew(Stack *s, int elemSize){
    assert(s->elemSize>0);
    s->elemSize = elemSize;
    s->logicalLen = 0;
    s->allocLen = 4;
    s->elems = malloc(4*elemSize);
    assert(s->elems != NULL);
}

void StackDispose(Stack *s){
    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--;
}

《深入理解計算機系統》讀書筆記(ch2)+ C 泛型