-
第一节 字节-类型-内存
- 一个bit可以储存一个0或1 一个字节是8 bit
- 而char类型占据一个字节,所以能表示256个字符。而short类型占两个字节。
- 赋值其实是按位拷贝,因此用short类型变量来赋值char类型时,只会保留short较低位的字节,高位的8bit会被舍弃。同理,用int类型(4个字节)给short变量赋值时,也只会将较低的16个bit保留,拷贝给short变量
- 但如果将一个值为2^15的int变量赋值给short,这时原本代表数量的bit变成了代表符号的bit……会有未知后果
- short s = -1 这里s+1就变成了全0,因此s所占的16个bit应该全是1(补码),这时如果将s赋给一个int类型的变量i呢?如果还是简单的按位拷贝然后将前面16个bit赋0, 符号位就被破坏了,因此要把符号位拉伸——前面16个bit也全赋1
- float浮点数的表示方法:32个bit,第一个是符号位s,中间八位是unsigned int(其表示的数记为exp,显然exp介于0-255之间),后23位是小数位(记为.xxxx)。那么这个float数所代表的值就是(-1)s**1.xxxx**2(exp-127)。
- 因此,如果int i = 5然后 float f = i ,这时5会被表示为1.25*2^2,也就是说exp为2+127,.xxxx为0.25
- 可以这么操作:float f =((float**)&i) 这样系统会把i所占的32个字节看成用上述方法表示的float类型数,打印f会发现f是一个极小的数!(exp为0, 所以f是1. xxx2^-127)*
- 如果, float f = 7.0;short s = ((short)&f); s会是什么呢? &f指向f所占32bit的首位,经过类型强制转换,&f所指向的内存的长度变为16bit,然后再将这16个bit按位拷贝给s!
-
第二节 操作内存
- 如何操纵数组中任意的内存单元 (例如对于一个int类型的数组,我们通常只能一次同时修改四个字节,但如果想单独修改其中某一个字节): 用强制类型转换,将指向数组的指针类型转换为占用内存更小的数据类型. 比如int arr[10]; ((char*) arr) +n就是指向第n个字节的指针!
- strdup() 是string duplicate 的缩写. 这个方法能够动态地分配足够的空间来存储字符串. 然后返回字符串第一个字符的地址.
- strcpy()第二个参数是字符串,第一个参数是要把字符串写入的地址,使用strcpy()时,会默认把第一个参数当作是任意长度字符序列空间基地址. strcpy函数会一个一个的把字符写入,包括最后的\0.
-
第三节 用纯C(无模板)来实现泛型编程:
-
写一个可以交换任意类型的两个变量的swap函数
void swap(void* vp1, void* vp2,int size){ // 不能 void temp = *vp1; //因为不能声明void类型变量,而且void* 也无法解引用,因为机器并不知道要提取多少个字节 char buffer[size];//大多数编译器不支持这个操作(数组大小为参数),可以换成动态申请内存 memcpy(buffer, vp1, size); memcpy(vp1, vp2, size); memcpy(vp2, buffer, size) }
- 这里的memcpy()是一个比strcpy()更通用的方法, 不要求拷贝的是字符, 不关心\0,因此必须显示地指明 要从内存位置上拷贝多少个字节.
-
search函数的泛型 lsearch(void *key, void *base, int n, int elemSize);
-
-
第四节 不用模版,实现泛型编程
- 不借助模板,实现可以搜索任意类型的数组的函数 lsearch()
void *lsearch(void *key,void*base,int n,int elemSize, int (*cmpFun)(void *, void *) // key 是要搜索的值的指针,base是搜索的数组,n是数组长度,cmpFun 函数用来比较是否相等 { for( int i = 0; i<n; i++){ void *elemAddr = (char*)base + i*elemSize; if(cmpFun(key,elemAddr)==0) return elemAddr; } return NULL; } **cmpFun的实现应根据具体情况,例如要比较整数时:** int intCmp(void *elem1, void *elem2){ //参数类型应该跟上面保持一致,用void* int *ip1 = elem1; //相当于将void*类型的参数强制转换为int*类型 int *ip2 = elem2; return *ip1 - *ip2; } **那如果要实现在字符串数组(二维)中,搜索一个字符串呢?** char *notes[] = { "Ab", "F#", "B", "Gb", "D"}; char *favoriteNote = "Eb" **这时的cmpFun就应该是** int StrCmp(void *vp1, void *vp2){ char *s1 = *(char**)vp1; char *s2 = *(char**)vp2; //还是将vp1vp2强制转换成本来的类型——favoriteNote和notes中的元素都是char*类型, //vp1,vp2作为他们的指针被传进来,类型应该是指针的指针 return strcmp(s1,s2); //strcmp()是内置函数 }
- strcmp()函数接收两个字符串,挨个比较每个字符,一旦遇到不相同的,就返回他们ASCII的差值
- 内置函数中有一个bsearch() 他的功能和参数与上面我们自己写的lsearch()相同,但用的是二分搜索。
typedef struct { int *elems; int logicalLen; int allocLength; }stack; void stackNew(stack *s); void stackDispose(stack *s);
- assert()接收一个布尔类型的参数,如果参数为真,则什么都不做,如果参数为假,则终止程序执行,并报告程序终止的位置
-
第五节 纯C(没有类)实现栈
typedef struct { int *elems; int logLen; //已经使用的内存 int allocLen; //申请的内存 }stack; void stackNew(stack *s){ //功能类似构造函数,其实只是顶层作用域中的普通函数 s->logLen = 0; s->allocLen = 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->logLen == s->allocLen){ //相比C++(不得不重新申请更大内存,然后将数组中的值挨个复制过来),C能够更好地操作底层内存 //可以调用**realloc()**函数,他可以调整之前用malloc分配的内存块大小 s->elems = realloc(s->elems,s->allocLen*2*sizeof(int)); s->allocLen *= 2; assert(s->elems!=NULL); } s->elems[s->logLen] = value; s->logLen++; } int stackPop(stack *s){ assert(s->logLen>0); s->logLen--; return s->elems[s->logLen]; }
- 关于realloc() : 用这个函数扩展之前申请单内存时,他首先检测当前内存后面还有没有空间,如果有则直接扩展,没有就重新找一块内存,再把原来的子节复制过来,接着把原来的内存释放掉,然后返回新内存的指针。 如果迭代时用到了realloc(),为了保持代码的一致性,在第一次申请内存时,我们可以不用malloc而使用realloc,然后第一个参数传NULL
-
第六节
- <
CS107 Computer Organization & Systems 笔记
最新推荐文章于 2022-09-20 20:59:37 发布