project1:设计一个 C 语言的动态扩容缓冲区
事实上,在很多优秀项目都会去封装一种接口,如
redis,git。现在你的目标是重新造一遍在轮子(belong
to you!):
这个缓冲区类的定义就免费送给你们啦:
struct strbuf {
int len; //当前缓冲区(字符串)长度
int alloc; //当前缓冲区(字符串)容量
char *buf; //缓冲区(字符串)
};
任务正式开始!
详细讲解放在代码中
partA
//初始化 sb 结构体,容量为 alloc
void strbuf_init(struct strbuf *sb, size_t alloc) {
sb->buf = (char *) malloc(sizeof(char) * (alloc + 1));
//不能用. sb并非结构名称
sb->len = 0;
sb->alloc = alloc;
}
//将字符串填充到 sb 中,长度为 len, 容量为 alloc
void strbuf_attach(struct strbuf *sb, void *str, size_t len, size_t alloc) {
sb->len = len;
sb->alloc = alloc;
// int i,slen;
// slen=strlen(str);
// for(i=0;i<slen;i++){
// sb->buf[i]=str[i];
// }
// sb->buf[sb->len]='\0';
sb->buf = (char *) str;
//强制类型转换,buf为指向字符的指针
}
//释放 sb 结构体的内存
void strbuf_release(struct strbuf *sb) {
free(sb->buf);
//free(sb);
}
//交换两个 strbuf
void strbuf_swap(struct strbuf *a, struct strbuf *b) {
int num1;
char *num2;
num1 = a->len;
a->len = b->len;
b->len = num1;
num1 = a->alloc;
a->alloc = b->alloc;
b->alloc = num1;
num2 = a->buf;
a->buf = b->buf;
b->buf = num2;
//结构体不像数组,是可以直接赋值的
//所以此段代码可以简化为如下形式:
// struct strbuf c=*a;
//*a=*b;
//*b=c;
}
//将 sb 中的原始内存取出,并将 sz 设置为其 alloc 大小
char *strbuf_detach(struct strbuf *sb, size_t *sz) {
*sz = sb->alloc;
return sb->buf;
//返回字符串
}
//比较两个 strbuf 的内存是否相同
int strbuf_cmp(const struct strbuf *first, const struct strbuf *second) {
if (first->len > second->len) {
return 1;
} else if (first->len < second->len) {
return -1;
} else {
return 0;
//返回0证明相同
}
}
//清空sb
void strbuf_reset(struct strbuf *sb) {
for(int i=0;i<sb->len;i++)
{
sb->buf[i]='\0';
}
sb->len=0;
}
partB
//确保在 len 之后 strbuf 中至少有 extra 个字节的空闲空间可用
void strbuf_grow(struct strbuf *sb, size_t extra) {
sb->buf = (char *) realloc(sb->buf, sb->len + extra + 1);
if (sb->alloc < sb->len + extra + 1) {
sb->alloc = sb->len + extra + 1;
}
> realloc:有时我们觉得我们用malloc,calloc函数申请的动态内存空间太大了,
> 有时觉得申请的空间太小了,为了合理使用内存,我们要对内存的大小做灵活的调整,
> 那么realloc函数就可以做到控制动态内存开辟的大小。
> realloc函数原型:
> void* realloc(void* memblock, size_t size)
> size_t size指的是New size in bytes,新的字节数,注意不是增加的字节数,而是新开辟的那块内存空间的字节数,返回值为调整之后的内存的起始地址
}
//向 sb 追加长度为 len 的数据 data
void strbuf_add(struct strbuf *sb, const void *data, size_t len) {
if(len==0)
{
return;//特殊情况没有数据
}
//判断是否扩容
if(sb->len+len>=sb->alloc)
{
sb->buf=(char *)realloc(sb->buf,sb->len+len+1);
sb->alloc=sb->len+len+1;
}
memcpy(sb->buf+sb->len,data,len);
sb->len+=len;
sb->buf[sb->len]='\0';
}
> memcpy函数是C/C++语言中的一个用于内存复制的函数,声明在 string.h 中(C++是 cstring)。其原型是:
>
> void *memcpy(void *destin, void *source, unsigned n);
> 作用是:以source指向的地址为起点,将连续的n个字节数据,复制到以destin指向的地址为起点的内存中。
> 函数有三个参数,第一个是目标地址,第二个是源地址,第三个是数据长度。
>
> 使用memcpy函数时,需要注意:
>
> 数据长度(第三个参数)的单位是字节(1byte = 8bit)。 注意该函数有一个返回值,类型是void*,是一个指向destin的指针。
//向 sb 追加一个字符 c
void strbuf_addch(struct strbuf *sb, int c) {
// if(sb->alloc==0){
// sb->alloc=2;
// }else{
// if (sb->alloc < sb->len+1) {
// sb->alloc = sb->alloc * 2;
// }
// }
// sb->len += 1;
// sb->buf = (char *) realloc(sb->buf, sb->alloc);
// sb->buf[sb->len-1] = c;
// sb->buf[sb->len] = '\0';
if(sb->alloc==0){
sb->alloc=2;//特殊情况
}
else{
if(sb->alloc<=sb->len+2)
{
sb->alloc*=2;
}
}
sb->buf=(char*)realloc(sb->buf,(sb->alloc));
sb->len+=1;
sb->buf[sb->len-1]=c;
sb->buf[sb->len]='\0';
}
//向 sb 追加一个字符串 s
void strbuf_addstr(struct strbuf *sb, const char *s) {
// while (sb->alloc < sb->len + strlen(s)+1) {
// sb->alloc *= 2;
// }
// sb->buf = (char *) realloc(sb->buf, sb->len + strlen(s) + 1);
// int len=strlen(s);
// sb->len+=len;
// memcpy(sb->buf,s,len);
strbuf_add(sb,s,strlen(s));
//sb->buf[sb->len]='\0';
}
//向一个 sb 追加另一个 strbuf的数据
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) {
strbuf_addstr(sb, sb2->buf);
}
//设置 sb 的长度 len
void strbuf_setlen(struct strbuf *sb, size_t len) {
// if (sb->alloc < len + 1) {
// sb->alloc = len + 1;
// }
sb->len = len;
//sb->buf = (char *) realloc(sb->buf, sb->len);
sb->buf[sb->len] = '\0';
}
//计算 sb 目前仍可以向后追加的字符串长度
size_t strbuf_avail(const struct strbuf *sb) {
return sb->alloc - sb->len - 1;
//-1非常重要,不要忘记'\0'
}
//向 sb 内存坐标为 pos 位置插入长度为 len 的数据 data
void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) {
sb->len += len;
if (sb->alloc < sb->len + 1) {
sb->alloc = sb->len + 1;
}
sb->buf = (char *) realloc(sb->buf, sb->alloc + 1);
char *p = (char *) malloc(sizeof(char) * (sb->len));//引入新变量
// char *t = (char *) malloc(sizeof(char) * (sb->len));
// char *s = (char *) malloc(sizeof(char) * (sb->len));
// int cnt=0;
// t = sb->buf;
// while (t <= sb->buf + pos) {
// *s = *t;
// t++;
// s++;
// }
// s[pos+1]='\0';
// while (*t != '\0') {
// *p = *t;
// t++;
// p++;
// cnt++;
// }
// p[cnt+1]='\0';
// strcat(s, (const char *) data);
// strcat(s, p);
// sb->buf=s;
strcpy(p,sb->buf+pos);
strcpy(sb->buf+pos,(const char*)data);
strcat(sb->buf,p);
free(p);//不free造成内存泄漏!
}
partC
//去除 sb 缓冲区左端的所有 空格,tab, '\t'
void strbuf_ltrim(struct strbuf *sb) {
char *s = sb->buf;//, *p = sb->buf;
while (*s != '\0' && (*s == ' ' || *s == '\t')) {
s++;
sb->len--;
}
memmove(sb->buf,s,sb->len);
> memmove()函数用于:移动内存块
> 函数声明:void * memmove ( void * destination, const void * source, size_t num );
> 从source复制num个字符到destination中
// while (*s != '\0') {
// *p++ = *s++;
// }
// p[sb->len] = '\0';
// sb->buf = p;
}
//去除 sb 缓冲区右端的所有 空格,tab, '\t'
void strbuf_rtrim(struct strbuf *sb) {
while (sb->buf[sb->len-1] == ' ' || sb->buf[sb->len-1] == '\t') {
sb->len--;
}
sb->buf[sb->len] = '\0';
}
// 删除 sb 缓冲区从 pos 坐标长度为 len 的内容
void strbuf_remove(struct strbuf *sb, size_t pos, size_t len) {
// char *p = (char *) malloc(sizeof(char) * (sb->len));
// char *t = (char *) malloc(sizeof(char) * (sb->len));
// t = sb->buf + pos + len;
// strcpy(p, sb->buf + pos + len);
// sb->len = pos + len;
// while (t != sb->buf + pos - 1) {
// sb->len--;
// t--;
// }
// sb->buf[sb->len] = '\0';
// strcat(sb->buf, p);
// sb->len -= len;
memmove(sb->buf+pos,sb->buf+pos+len,sb->len-len-pos);
sb->len=sb->len-len;
}
partD
//sb 增长 hint ? hint : 8192 大小, 然后将文件描述符为 fd 的所有文件内容追加到 sb 中
ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) {
sb->buf = (char *) realloc(sb->buf, (sb->alloc += hint ? hint : 8192));
sb->alloc += hint ? hint : 8192;
FILE *fp = fdopen(fd, "r");
if (fp != NULL) {
char ch;
while ((ch = fgetc(fp)) != EOF) {
sb->buf[sb->len++] = ch;
}
sb->buf[sb->len] = '\0';
}
return sb->len;
}
//将 将文件句柄为 fp 的一行内容(抛弃换行符)读取到 sb 检查长度是否超过缓冲区
int strbuf_getline(struct strbuf *sb, FILE *fp) {
int ch;
//int count=0;
while ((ch = fgetc(fp)) != EOF) {
if( (ch == '\n')||(feof(fp)!=0)){
break;
}
if((strbuf_avail(sb)>=1)){
strbuf_addch(sb,ch);
}else if((strbuf_avail(sb)<1)){
sb->buf = (char *) realloc(sb->buf, sb->alloc+1);
strbuf_addch(sb,ch);
// sb->alloc+=1;
// sb->buf[sb->len++] = ch;
}
// sb->buf = (char *) realloc(sb->buf, sb->alloc);
// sb->buf[sb->len++] = ch;
}
//sb->buf[sb->len] = '\0';
return sb->len;
}
无信用挑战练习
//将长度为 len 的字符串 str 根据切割字符 terminator 切成多个 strbuf,并从结果返回,max 可以用来限定最大切割数量。
//返回 struct strbuf 的指针数组,数组的最后元素为 NULL
struct strbuf **strbuf_split_buf(const char *str, size_t len, int terminator, int max) {
struct strbuf **buf_link = (struct strbuf **) malloc(sizeof(struct strbuf *) * (max+1));
const char *end=str+len;
const char *head=str;
const char *p;
int count=0;//计数不能超过max
while(*head==terminator)
head++;
//跳过开头的目标字符
for (p=head;p<=end;p++) {
//在指针没有指到末尾时一直向前移动
if (*p==terminator||p==end) {
//遇到切割字符开始切割
int len_tmp;
len_tmp=p-head;
buf_link[count]=(strbuf*)malloc(sizeof(strbuf));//!非常重要,动态分配内存
buf_link[count]->len=len_tmp;
buf_link[count]->alloc=len_tmp+1;//为'\0'留下空间
buf_link[count]->buf=(char *)malloc(sizeof(char)*(len_tmp+1));//for '\0'
memcpy(buf_link[count]->buf,head,len_tmp);
//内存复制函数
buf_link[count]->buf[len_tmp]='\0';
count++;
//buf_link[count++]->buf[len_tmp]='\0';
while (*p==terminator&&p<=end){
p++;//找到下一次开始的地方
}
head=p;//切割完又重新开始,寻找下一个切割字符
}
if (count==max)
break;
}
buf_link[count]=NULL;
return buf_link;
}
//target_str : 目标字符串,str : 前缀字符串,strlen : target_str 长度 ,前缀相同返回 true 失败返回 false
bool strbuf_begin_judge(char *target_str, const char *str, int len) {
int i;
// for (i = 0; i < strlen(str); i++) {
// if(len==0){
// return true;
// }
// if(strlen(str)>len){
// return false;
// }
// if (target_str[i] != str[i]) {
// //flag=1;
// return false;
// }
// }
// // if(flag==0){
// // return true;
// // }
// return true;
if (len == 0)
return true;//特殊情况
if (strlen(str) > len)
return false;//粗略判断
for (int i = 0; i < strlen(str); i++) {
if (str[i] != target_str[i]) {
return false;
}
}
//不同返回false,跳出for循环后则返回true
return true;
}
//target_str : 目标字符串,begin : 开始下标,end 结束下标。
//len : target_buf的长度,参数不合法返回 NULL. 下标从0开始,[begin, end)区间。
char *strbuf_get_mid_buf(char *target_buf, int begin, int end, int len) {
if (begin < 0 || end < 0 || end > len || len < 0) {
return NULL;
} else {
char *p = (char *) malloc(sizeof(char *) * (end - begin + 1));
int i = begin, j = 0;
for (i = begin, j = 0; i < end; i++, j++) {
*(p + j) = *(target_buf + i);
}
//memcpy(p, target_buf + begin, end - begin);
p[end - begin] = '\0';
return p;
}
}