plan2任务总结
任务相关
任务要求地址:https://github.com/xiyou-linuxer/Plan/blob/main/project/strbuf.md
测试文件地址:https://github.com/xiyou-linuxer/Exercise/blob/main/strbuf/test.cpp
代码本体
//A
void strbuf_init(struct strbuf* sb, size_t alloc) {
sb->len = 0;
sb->alloc = alloc;
if (alloc)//当传入的alloc为0时防止内存泄漏,测试数据4
sb->buf = (char*)malloc(sizeof(char) * alloc);
}
void strbuf_attach(struct strbuf *sb, void *str, size_t len, size_t alloc)
{
sb->alloc=alloc;
sb->len=len;
sb->buf=(char*)str;
sb->buf[sb->len]='\0';
}
void strbuf_release(struct strbuf *sb)
{
free(sb->buf);
sb->buf=NULL;//释放空间后需将指针赋值为空,防止其成为野指针影响后续操作
}
void strbuf_swap(struct strbuf *a, struct strbuf *b)
{
int t;
char *p;
t=a->len;
a->len=b->len;
b->len=t;
t=a->alloc;
a->alloc=b->alloc;
b->alloc=t;
p=a->buf;
a->buf=b->buf;
b->buf=p;
}
char *strbuf_detach(struct strbuf *sb, size_t *sz)
{
char *d;
d=sb->buf;
*sz=sb->alloc;
return d;
}
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;
}
void strbuf_reset(struct strbuf *sb)
{
int i;
for(i=0;i<sb->len;i++){
sb->buf[i]='\0';
}
sb->len=0;
}
//B
void strbuf_grow(struct strbuf *sb, size_t extra)
{
if(sb->alloc-sb->len-1 > extra)//判断容量是否充足,若是容量充足就直接返回
return ;
else
{
sb->buf=(char *)realloc(sb->buf,(sb->alloc+extra)*sizeof(char));
sb->alloc=(sb->alloc+extra);
}
return ;
}
void strbuf_add(struct strbuf *sb, const void *data, size_t len)
{
strbuf_grow(sb,len+1);//调用grow函数扩容
memcpy(sb->buf+sb->len,data,len);//将读入的数据写入到strbuf中
sb->len=sb->len+len;
sb->buf[sb->len]='\0';
return ;
}
void strbuf_addch(struct strbuf *sb, int c)
{
strbuf_grow(sb,2);
sb->buf[sb->len]=c;
sb->len++;
sb->buf[sb->len]='\0';
return ;
}
void strbuf_addstr(struct strbuf *sb, const char *s)//以下代码功能相差不大均可通过调用前面已有的代码完成实现
{
strbuf_add(sb,s,strlen(s));
}
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)
{
strbuf_addstr(sb,sb2->buf);
}
void strbuf_setlen(struct strbuf *sb, size_t len)
{
sb->len=len;
sb->buf[len]='\0';//将后续内容清除
}
size_t strbuf_avail(const struct strbuf *sb)
{
int t;
if(sb->alloc==0){
//当alloc等于0时说明无可用空间
t=0;
}else{
t=sb->alloc-sb->len-1;
}
return t;
}
void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len)
{
int i,j;
if(sb->len+pos+len+1>sb->alloc){
//判断容量是否充足
strbuf_grow(sb,pos+sb->len+1);
}
for(i=sb->len;i>=pos;i--){
sb->buf[i+len]=sb->buf[i];
}//先将插入处往后的所有字符赋值到插入字符串结束的位置
for(i=pos,j=0;i<pos+len;i++,j++){
//再将要插入的字符串赋值给buf将原有位置的值覆盖
sb->buf[i]=((char *)data)[j];
}
sb->len+=len;
}
//c
void strbuf_ltrim(struct strbuf *sb)
{
char *s=sb->buf;
while(*s==' '||*s=='\t'){
//跳过左边的所有空格
sb->len--;
s++;
}
memmove(sb->buf,s,sb->len);//将空格后的字符赋值到有空格处将其覆盖
sb->buf[sb->len]='\0';//令结尾处为'\0'结束
}
void strbuf_rtrim(struct strbuf *sb)
{
while(sb->buf[sb->len-1]==' '||sb->buf[sb->len-1]=='\t'){
sb->len--;
}//直接跳过右边的所有空格
}
void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
{
memmove(sb->buf+pos,sb->buf+pos+len,sb->len-pos-len);
sb->len=sb->len-len;
sb->buf[sb->len]='\0';
}
//d
ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
{
FILE *fp;
char a;
if(((fp=fdopen(fd,"r"))==NULL)||(a=fgetc(fp))==EOF){
//如果没有待读入的文件或该文件中没有内容,就直接返回当前len的大小
return sb->len;
}else{
sb->alloc+=(hint?hint:8192);
sb->buf=(char*)realloc(sb->buf,sizeof(char)*(sb->alloc));//将字符串扩容至所需大小
sb->buf[sb->len++]=a;
while((a=fgetc(fp))!=EOF){
//当未到文件结尾时将文件中的字符逐一读入并赋值给buf
sb->buf[sb->len]=a;
sb->len++;
}
sb->buf[sb->len]='\0';
return sb->len;
}
}
int strbuf_getline(struct strbuf *sb, FILE *fp)
{
int cnt=0,i;
while(((i=fgetc(fp))=='\n'||i==EOF)){
//当还为读到这句话末尾,或该文档末尾就将此处的字符串写入buf中
strbuf_addch(sb,i);
cnt++;
}
sb->len=cnt;
return 1;
}
//challenge1
struct strbuf **strbuf_split_buf(const char *str, size_t len, int terminator, int max)
{
char q=(char)terminator;
int count=0;
struct strbuf** p=(struct strbuf**)malloc(sizeof(struct strbuf*));//为指针数组开辟空间
struct strbuf* j;//定义结构体指针j
char s[len+1];
memcpy(s,str,len+1);
for(int i=0;i<len;i++)//防止开头处就出现‘\0’使strtok误以为该字符串为空
{
if(s[i]=='\0')
{
s[i]='@';
}
}
char* r=strtok(s,&q);
while(r!=NULL&&count<max)
{
int r_len=strlen(r);
for(int i=0;i<r_len;i++)
{
if(r[i]=='@')
{
r[i]='\0';
}
}
j=(struct strbuf*)malloc(sizeof(struct strbuf));//为该结构体指针动态分配内存
strbuf_init(j,r_len+1);//初始化j指向的结构体
strbuf_add(j,r,r_len);//将切割后返回的字符串r写入到j指向的结构体内的buf中
p[count++]=j;
p=(struct strbuf**)realloc(p,sizeof(struct strbuf*)*(count+1));
r=strtok(NULL,&q);
}
p[count]=NULL;
return p;
}
//challenge2
bool strbuf_begin_judge(char* target_str, const char* str, int strlen)
{
int i;
for(i=0;i<strlen;i++){
if(str[i]=='\0'){
break;
}
if(target_str[i]!=str[i]){
return false;
}
}
return true;
}
//challeng3
char* strbuf_get_mid_buf(char* target_buf, int begin, int end, int len)
{
if(end<begin||target_buf==NULL){
return NULL;
}
char *str;
str=(char *)malloc(sizeof(char)*(end-begin+1));
str=(char *)memcpy(str,target_buf+begin,end-begin);
str[end-begin]='\0';
return str;
}
完成过程中的一些问题
memmove函数
主要作用:将一一串字符复制到字符数组当中想要的位置
memmove(sb->buf,s,sb->len);//第一个参数为需要复制到的地址,第二个参数为开始复制的地址,第三个参数为复制字节的长度
strtok函数
strtok(str,ch);//第一个参数为需要被切割的字符串,第二个参数为切割时识别的字符
该函数的返回值为被切割后的前半段
while( p!=NULL ){
p = strtok(NULL, "$"); //若是第一个参数为NULL该函数会自动将第一个参数当作被切割后的后半段
if( p==NULL ){
break;
}
}
关于指针与malloc
**1.**通过malloc申请的空间得到的指针不能将其他地址赋给该指针,只能将该地址上储存的值赋给在malloc来的空间中的值否则在释放内存以及在后续的realloc或calloc操作中会出现问题。free只能释放malloc得到的初始地址若将其他地址赋于该指针会导致无法释放,realloc等同理
2.malloc得到的空间要及时找到合适的时机将其释放,防止内存泄漏的发生
**3.**指针释放后需要将其置空,防止其变为野指针
释放时可以释放空指针但不能释放野指针
4.野指针的危害
(1)指向一处不可访问的空间从而引发段错误
(2)指向了一处程序中恰好需要的空间,若此时正在对该空间上的变量进行操作,可能会因为对于野指针赋值等的操作造成冲突使程序崩溃或者数据损坏
(3)指向一个可用的但没有被使用的空间,虽然当时并不会对程序造成任何影响,但在后续程序的改进与维护过程中会埋下不易发现的隐患
关于内存泄漏
造成原因:
**1.**动态申请内存前不判断是否需要申请内存
**2.**动态申请完不检查是否成功分配内存
**3.**不及时对申请的内存初始化,导致垃圾值的产生
**4.**动态申请内存后不释放大量空间被占用
**5.**访问申请后的内存时越界
危害:
长期运行会导致内存溢出,最终使整个程序运行极慢甚至彻底崩溃卡死。