編程範式學習筆記

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

void swap(void *vp1,void *vp2,int size)
{
	char *buffer=(char *)malloc(size*sizeof(char));
	memcpy(buffer,vp1,size);
	memcpy(vp1,vp2,size);
	memcpy(vp2,buffer,size);
}

void main()
{
	char *h=strdup("William");
	char *w=strdup("Lily");
	swap(&h,&w,sizeof(char *));
	printf("%s\n",h);
}


    開始對swap交換裏面的&比較奇怪。但後來又想,如果我們交換一個int,那麼穿進去的是int的地址,如果我們交換的是char *那麼理所當然的傳進去的是char* 的地址。

    如果這裏沒有&,它的運行結果將是

   

    很明顯只交換了前面四個字符,因爲這是對字符串直接進行操作。

    當然,也可以這麼傳。swap(h,&w,sizeof(char *))一個是*一個是**,但是編譯器還是不會報錯。最可能的情況是將h的前四個字節解釋成地址,而在棧中沒有這個地址,導致程序崩潰。


    

int lsearch(int key,int array[],int size)  //這是int型的線性搜索
{
	for(int i=0;i<size;i++)
	{
		if(array[i]==key)
			return i;
	}
	return -1;
}
void *lsearch(void *key,void *base,int n,int elemsize)     
{
	for(int i=0;i<n;i++)
	{
		void *elemAddr=(char *)base+i*elemsize;
		if(memcpy(key,elemAddr,elemsize)==0)
			return elemAddr;
	}
	return NULL;
}

   需要注意的是

   void *elemAddr=(char *)base+i*elemsize;

   將base強制轉換成char*是爲了能進行算術運算。

   不允許對void*解引用,因爲你不知道它在內存中佔幾個字節

   因爲base是void*類型的,如果使用base+i的話,編譯器不會隱式的將i*sizeof(類型),因爲編譯器不知道這個類型

   而且使用memcpy(key,elemAddr,elemsize)==0爲判定條件也是不好的,因爲需要考慮函數指針(這裏也不知道說的是什麼意思)

   實際上的函數原型是這樣的

   viod *lsearch(viod *key,void *base,int n,int elemSize,int (*cmpfn)(void*,void*))

   

     int array[]={4,2,3,7,11,6};
     int size=6;
     int number=7;
     int *fund=(int *)lsearch(&number,array,size,sizeof(int),intCmp);
   以一個整型數組的線性查找爲例

   key指向number的首地址

   base指向數組的首地址

   它們都不知道所指向的地址會被解釋成什麼

   n是數組長度

   elemsize是顯示傳進去的數據類型長度,也因爲它的存在使函數具備了泛型的基本功能

   cmpfn是個函數指針,指向的是能比較兩個地址表現的函數。

   這裏使用


int intCmp(void *elem1,void *elem2)
{
	int *vp1=(int *)elem1;
	int *vp2=(int *)elem2;
	return *vp1-*vp2;
}

作爲比較函數原型

當是下面這個例子的時候,比較原型又會有很大的不同

    char *notes[]={"Ab","F#","B","Gb","D"};
    char *favouriteNote="Eb";
    char **fund=lsearch(&favouriteNote,notes,5,sizeof(char*),StrCmp);
int StrCmp(void *vp1,void *vp2)
{
	char *s1=*(char **)vp1;
	char *s2=*(char **)vp2;
	return strcmp(s1,s2);
}

因爲notes是char**的形式,爲了使比較雙方等同,故傳進去的是&favouriteNote,當然也可以不這麼傳,但是相應的下面就得做出一些修改

講void *形式的&favouriteNote和notes強制轉換成char**的形式再解引用就相當於指向字符串數組的指針

這時候在調用strcmp函數進行比較就很順理成章了。


函數和方法的不同之處在於,它們看起來很相似,只是方法實際上將其相關對象的地址作爲一個隱含參數,這個隱含參數叫做(this)指針

lsearch的第五個參數傳入的要麼是一個普通的非面向對象的函數,要麼就是靜態方法,因爲靜態方法沒有this指針


stack的pure c實現

only for int

typedef struct {
	int *elems;
	int logicalLen;
	int alloclength;
}Stack;

void StackNew(Stack *s);
void StackDispose(Stack *s);
void StackPush(Stack *s,int value);
int StackPop(Stack *s);

void StackNew(Stack *s)
{
	s->logicalLen=0;
	s->alloclength=4;
	s->elems=malloc(4*sizeof(int));
	assert(s->elems!=NULL);  //這一句很重要,嚴謹性的體現
}

void StackDispose(Stack *s)
{
	free(s->elems);
}

void StackPush(Stack *s,int value)
{
	if(s->logicalLen==s->alloclength)
	{
		s->alloclength*=2;
		s->elems=realloc(s->elems,s->alloclength*sizeof(int));
		assert(s->elems!=NULL);	
	}
	s->elems[s->logicalLen]=value;
	s->logicalLen++;
}

int StackPop(Stack *s)
{
	assert(s->logicalLen>0);
	s->logicalLen--;
	return s->elems[s->logicalLen];
}

對於泛型

typedef struct{
	void *elems;
	int elemSize;
	int loglength;
	int alloclength;
	;
}Stack;

void StackNew(Stack *s,int elemSize);
void StackDispose(Stack *s);
void StackPush(Stack *s,void *elemAddr);
void StackPop(Stack*s,void *elemAddr);

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

void StackDispose(Stack *s)
{
	free(s->elems);
}

static void StackGrow(Stack *s)
{
	s->alloclength*=2;
	s->elems=realloc(s->elems,s->alloclength*s->elemSize);
}
void StackPush(Stack *s,void *elemAddr)
{
	if(s->loglength==s->alloclength)
		StackGrow(s);
	void *target=(char *)s->elems+s->loglength*s->elemSize;
	memcpy(target,elemAddr,s->elemSize);
	s->loglength++;
}

void StackPop(Stack *s,void *elemAddr)
{
	void *sourse=(char *)s->elems+(s->loglength-1)*s->elemSize;
	memcpy(elemAddr,sourse,s->elemSize);
	s->loglength--;
}

用這個泛型實現對字符串進行操作

void main()
{
	const char* friends[]={"AI","Bob","Carl"};
	Stack stringStack;
	StackNew(&stringStack,sizeof(char *));
	for(int i=0;i<3;i++)
	{
		char *copy=strdup(friends[i]);
		StackPush(&stringStack,©);
	}
	char *name;
	for(int i=0;i<3;i++)
	{
		StackPop(&stringStack,&name);
		printf("%s\n",name);
		free(name);
	}
	StackDispose(&stringStack);
}


但是現在問題來了

        for(int i=0;i<3;i++)
	{
		StackPop(&stringStack,&name);
		printf("%s\n",name);
		free(name);
	}
如果將這幾句註釋掉,這時候調用StackDispose棧裏還有元素

棧不應該強制的設爲空,或者說用戶並需要彈出所有元素才能釋放棧空間

爲了方便用戶我們應該在釋放棧空間之前將堆的內容釋放掉,並將它們歸還給堆空間,很多情況下我們沒必要這麼做,當存儲的是內置類型的時候沒必要手動清零,不過需要注意歸還各類動態申請的資源,或者一些打開文件資源等

現在的stackDispose函數並不知道棧裏的那些元素是指針,最多隻知道他們佔四個字節。那麼怎麼讓free釋放掉所有的資源呢

我們需要重寫棧的定義,重寫StackNew,重寫StackDispose,寫一個釋放內存的函數freefn

void StackNew(Stack *s,int elemSize,void (*freefn)(void *))

void StackDispose(Stack *s)
{
	if(s->freefn!=NULL)
		for(int i=0;i<s->loglength;i++)
		{
			s->freefn((char *)s->elems+i*s->elemSize);
		}
	free(s->elems);
}

void stringFree(void *elem)
{
	free(*(char **)elem);
}




內存分配的空間實際上比你需要的空間要大一些,因爲前面的4個或8個字節需要記錄分配空間的大小

當你得到返回的頭指針時,實際上得到的不是整塊內存的頭指針,而是頭指針之後4個或8個字節之後的指針


每一個寄存器在理論上都可以從RAM讀取信息或將信息寫入RAM中

ALU算數邏輯單元,它很容易在4個字節上進行加法減法移位等操作

ALU與寄存器堆相連

上述結構要求所有有意義的數學運算都需要通過寄存器來完成


另一種方式是讓ALU與整個RAM相連,這樣的話就會特別昂貴


大多數的運算都是將數據讀入寄存器中,然後將計算結果讀入另一個寄存器,最後再寫入到屬於它的內存中

LOAD STORE STRUCTURE

--------------------------------------------------------------------------------------------------------------------------------------------

發現整個文章的結構都很混亂,等全部弄完了再整理吧。

CLASS 9  主要講的是怎麼將C C++的語句轉換爲彙編語句

int i;
int j;
i=10;
j=i+7;
j++;
/*----------------------------------------------*/
M[R1+4]=10;  //大寫的M代表整個RAM   store operation
R2=M[R1+4];  //load operation
R3=R2+7;     //ALU operation
M[R1]=R3;	 //store operation
/*----------------------------------------------*/
R2=M[R1];
R2=R2+1;
M[R1]=R2;
//ALU默認對四個字節進行處理

int i;
short s1;
short s2;
i=200;  => M[R1+4]=200;
s1=i;   => R2=M[R1+4]; M[R1+2]=.2 R2;
s2=s1+1;=> R2=.2M[R1+2]; R3=R2+1; M[R1]=.2 R3;

.2表示低位的兩個字節

int array[4];
int i;
for(i=0;i<4;i++)
	array[i]=0;
i--;
/*---------------------------------------------*/
M[R1]=0;   //i=0
R2=M[R1];
BGE R2,4,PC+40
R3=M[R1];
R4=R3*4;  //基地址
R5=R1+4;  //偏移量
R6=R4+R5;
M[R6]=0;
R2=M[R2];
R2=R2+1;
M[R1]=R2;
JMP PC-40;

//所有的彙編指令都是四個字節寬
//彙編中共有59條指令
//所以前面的六位用來表示指令類型,然後再決定怎麼解釋後面的26位

BGE  B表示分支 GE表示大於或等於 PC+40表示下跳到10條指令之後

CLASS 10

void foo(int bar,int *baz)
{
	char snack[4];
	short *why;	
}

每次調用這個函數都會在棧中產生下面的結構


中間的結構保存function called 信息

void foo(int bar,int *baz)
{
	char snack[4];
	short *why;	
	why=(short *)(snack +2);
	*why=50;
}



int main(int argc,char **argv)
{
	int i=4;
	foo(i,&i);
	return 0;
}



活動記錄的上部分是由調用者設置的

對於下面的一半,調用者並不知道函數中到底有多少局部變量

/*----
SP=SP-4;
M[SP]=4;
SP=SP-8;
R1=M[MP+8];
R2=MP+8;
M[SP]=R1;
M[SP+4]=R2;
CALL <foo>
SP=SP+8;

FOO:
SP=SP-8;
R1=SP+6;
M[SP]=R1;
R1=M[SP];
M[R1]=.2 50;
SP=SP+8;
RET;

MAIN:
RV=0;   //rv專門在調用和被調用函數間返回返回值



發佈了40 篇原創文章 · 獲贊 4 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章