#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專門在調用和被調用函數間返回返回值