本章主要介绍各类型的机器表示,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--; }
原文:https://www.cnblogs.com/42-Curry/p/9824727.html