設計一個C語言的動態(tài)擴容緩沖區(qū)-創(chuàng)新互聯(lián)

知識要點
  1. 字符串
  2. 面向對象的C語言設計
  3. 動態(tài)內存分配
  4. Linux 文件 API
  5. 獲取線路


正文
struct strbuf {int len;     //當前緩沖區(qū)(字符串)長度
  int alloc;   //當前緩沖區(qū)(字符串)容量
  char *buf;   //緩沖區(qū)(字符串)
};

HINT:

創(chuàng)新互聯(lián)從2013年創(chuàng)立,先為武陟等服務建站,武陟等地企業(yè),進行企業(yè)商務咨詢服務。為武陟企業(yè)網站制作PC+手機+微官網三網同步一站式服務解決您的所有建站問題。
  • strbuf 的成員 len 代表的是 buf 緩沖區(qū)的長度,每次我們將字符串追加入 strbuf 中,我們都應該使用 strbuf_setlen() 去更新 strbuf 的長度 len,注意 123\0456 的長度不是 3,而是 7。
  • strbuf 的成員 alloc 代表的是 buf 緩沖區(qū)的容量,也就是我們每次動態(tài)分配的數(shù)組大小,每當我們需要向 sb 內追加一個字符串,我們需要計算當前的字符串長度加上追加的字符串長度,如果超過了當前的容量,我們就需要把容量擴大一倍,然后將字符串添加進去。

在這里插入圖片描述

//初始化 sb 結構體,容量為 alloc。
void strbuf_init(struct strbuf *sb, size_t alloc)//數(shù)據類型解釋見下
{sb->len=0;
    sb->alloc=alloc;
    sb->buf=(char *)malloc(sizeof(char)*alloc);//強制類型轉換(void*--char*)
    strcpy(sb->buf,"");
}


//將字符串填充到 sb 中,長度為 len, 容量為 alloc。
void strbuf_attach(struct strbuf *sb, void *str, size_t len, size_t alloc)
{sb->buf=(char*)str;//強制類型轉換(void*--char*)
    sb->len=len;
    sb->alloc=alloc;
}


//釋放 sb 結構體的內存。
void strbuf_release(struct strbuf *sb)
{free(sb->buf);//參數(shù)是malloc()返回的地址,當時用buf存該地址
    sb->buf=NULL;//把指針設為NULL
    sb->len=0;
    sb->alloc=0;
}


//交換兩個 strbuf。
void strbuf_swap(struct strbuf *a, struct strbuf *b)
{struct strbuf t;
    
法一:
    t=*a;
    *a=*b;
    *b=t;
    
法二:
    t.len=a->len;
    t.alloc=a->alloc;
    t.buf=a->buf;
    
    a->len=b->len;
    a->alloc=b->alloc;
    a->buf=b->buf;

    b->len=t.len;
    b->alloc=t.alloc;
    b->buf=t.buf;
    
}


//將 sb 中的原始內存取出,并將 sz 設置為其 alloc 大小 。
char *strbuf_detach(struct strbuf *sb, size_t *sz)
{*sz=sb->alloc;//sz=&(sb->alloc)編譯錯誤
                  //因為cannot convert ‘int*’ to ‘size_t*’ {aka ‘long unsigned int*’} in assignment
    return sb->buf;//注意函數(shù)的返回類型
}


//比較兩個 strbuf 的內存是否相同。
int strbuf_cmp(const struct strbuf *first, const struct strbuf *second)
{// int tag=1;錯
    // if(first->len!=second->len)tag=0;
    // if(first->alloc!=second->alloc)tag=0;
    // if(strcmp(first->buf,second->buf)!=0)tag=0;
    // return tag;//相同‘1’,不同‘0’
    
    if(first->len>second->len)
        return 1;
    if(first->lenlen)
        return -1;
    else 
        return 0;
}


//清空 sb 。
void strbuf_reset(struct strbuf *sb)//清空指的是讓內存沒有東西,而非釋放(128G內存恢復出場設置)
{// sb->len=0;錯
    // sb->alloc=0;
    // free(sb->buf);
    // sb->buf=NULL;

法一:
    int i; 
    for(i=0;ilen;i++){sb->buf[i]='\0';
     }
     sb->len=0;
    // sb->alloc=0  因為alloc是申請到的內存大小,所以不能令為零(否則恢復出場設置后128G空間都沒了)

法二:
    memset(sb->buf,0,sb->len);//函數(shù)解釋見下
    sb->len=0;
    
}

size_t

無符號整數(shù),32位下為unsigned int,64位下為unsigned long
在數(shù)組下標和內存管理函數(shù)之類的地方廣泛使用

memset

void *memset(void *s, int c, size_t n);

  • s指向要填充的內存塊。
    c是要被設置的值。
    n是要被設置該值的字符數(shù)。
    返回類型是一個指向存儲區(qū)s的指針。

在這里插入圖片描述

//確保在 len 之后 strbuf 中至少有 extra 個字節(jié)的空閑空間可用。
  //(128G總內存空間在已用100G后,確保至少有30G空閑空間可以用)->如果不夠,再申請一塊內存(再買內存卡)
void strbuf_grow(struct strbuf *sb, size_t extra)
{if(sb->alloc-sb->len< extra){sb->buf=(char*)realloc(sb->buf,sb->alloc+extra);//默認buf之前申請過一塊內存,返回值為剛申請到的內存首地址
        sb->alloc += extra;                             //函數(shù)解釋見下
    }
}


//向 sb 追加長度為 len 的數(shù)據 data 。
void strbuf_add(struct strbuf *sb, const void *data, size_t len)
{// if(sb->len+len<= sb->alloc){ //錯
    //     sb->buf=strcat(sb->buf,(const char*)data);//(要不要強制類型轉換為char*?)要!
    // }
    // else {//     strbuf_grow(sb,sb->alloc);//再來一倍
    //     sb->buf=strcat(sb->buf,(const char*)data);
    // }
    // sb->len+=len;
    
法一:
    if(sb->len+len >= sb->alloc)
    {sb->buf=(char*)realloc(sb->buf,sb->len+len+1);//包括\0
        sb->alloc=sb->len+len+1;//更新擴容后的內存大小256G
    }
    
法二:
	strbuf_grow(sb->buf,len+1);
	
    memcpy(sb->buf+sb->len,data,len);//函數(shù)解釋見下
    sb->len+=len;
    // sb->buf+sb->len='\0'; //錯
    //*(sb->buf+sb->len)='\0';  //ok!
    sb->buf[sb->len]='\0';

}


//向 sb 追加一個字符 c。
void strbuf_addch(struct strbuf *sb, int c)
{// char a[1];
    // a[0]=c;
	// if(sb->len+1<= sb->alloc)
	//     sb->buf=strcat(sb->buf,a);
	// else {//     strbuf_grow(sb,sb->alloc);
	//     sb->buf=strcat(sb->buf,a);
	// }
	// sb->len+=1;

法一:
    if(sb->len+1 >= sb->alloc){sb->buf=(char*)realloc(sb->buf,(sb->alloc)*2);//內存翻兩倍
        sb->alloc=sb->alloc*2;
    }
    memcpy(sb->buf+sb->len,&c,1);
    sb->len+=1;
    sb->buf[sb->len]='\0';

法二:
	strbuf_add(sb,&c,1);

}


//向 sb 追加一個字符串 s。
void strbuf_addstr(struct strbuf *sb, const char *s)
{法一:
    int n=strlen(s);
    if(sb->len+n >= sb->alloc){sb->buf=(char*)realloc(sb->buf,sb->len+n+1);
        sb->alloc=sb->len+n+1;
    }
    memcpy(sb->buf+sb->len,s,n);
    // sb->buf=strcat(sb->buf,s); 錯->解釋見下
    sb->len+=n;
    sb->buf[sb->len]='\0';

法二:
	strbuf_add(sb,s,strlen(s));
}


//向一個 sb 追加另一個 strbuf 的數(shù)據。
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)//追加一個結構體
{法一:
    if(sb->len+sb2->len >= sb->alloc){sb->buf=(char*)realloc(sb->buf,sb->len+sb2->len+1);
        sb->alloc=sb->len+sb2->len+1;
    }
    sb->buf=strcat(sb->buf,sb2->buf);
    sb->len+=sb2->len;

法二:
	strbuf_addstr(sb,sb2->buf);
}


//設置 sb 的長度 len。
void strbuf_setlen(struct strbuf *sb, size_t len)
{if(len >= sb->alloc)//(要安裝應用,先判斷是否裝的下)
    {sb->buf=(char*)realloc(sb->buf,len+1);
        sb->alloc=len+1;
    }
    sb->len=len;
    sb->buf[sb->len]='\0';
}


//計算 sb 目前仍可以向后追加的字符串長度。
size_t strbuf_avail(const struct strbuf *sb)
{return (sb->alloc-(sb->len+1));//末尾的\0
}


//向 sb 內存坐標為 pos 位置插入長度為 len 的數(shù)據 data 。
void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len)
{if(sb->len+len >sb->alloc){strbuf_grow(sb,len+1);
    }
    // int i;錯
    // char a[len];
    // strcpy(a,(const char*)data);
    // for(i=0;i//     sb->buf[pos+i]=a[i];  //默認原來位置沒有數(shù)據->不行
    // }
    memmove(sb->buf+pos+len,sb->buf+pos,sb->len-pos);//把pos位置后面的數(shù)據移到pos+len的地方,空出來的len長度來存放data,函數(shù)解釋見下
    memcpy(sb->buf+pos,data,len);//sb->buf[pos]錯
    sb->len+=len;
    sb->buf[sb->len]='\0';
}

realloc

void* realloc(void* memblock, size_t size)

  • memblock: 先前開辟的內存塊的指針(也就是malloc或calloc之前申請的那塊內存空間,即需要調整大小的內存空間)
  • size: New size in bytes,新的字節(jié)數(shù),注意不是增加的字節(jié)數(shù),而是新開辟的那塊內存空間的字節(jié)數(shù)(sizeof(…))
  • 返回值: 新內存的起始地址(一般要xx=realloc)

memcpy

void *memcpy(void *dest, void *source, size_t n)

  • dest:指向用于存儲復制內容的目標數(shù)組
  • source:指向要復制的數(shù)據源
  • n :要被復制的字節(jié)數(shù)(sizeof(…))
  • 返回值:指向dest的指針(一般不用xx=,而是直接調用即可)

1.正常在復制字符串時用strcpy,而需要復制其他類型數(shù)據時則一般用memcpy
2.strcpy不需要指定長度,它遇到被復制字符的串結束符"\0"才結束,所以容易溢出。memcpy則可控制需要復制長短n
3.source和destin所指的內存區(qū)域可能重疊,但是如果source和dest所指的內存區(qū)域重疊,那么這個函數(shù)并不能夠確保source所在重疊區(qū)域在拷貝之前不被覆蓋。而使用memmove可以用來處理重疊區(qū)域。

memmove

void *memmove(void *dest, const void *source, size_t n)

  • dest:指向用于存儲復制內容的目標數(shù)組
  • source:指向要復制的數(shù)據源
  • n:要被復制的字節(jié)數(shù)
  • 返回值:指向dest的指針(一般不用xx=,而是直接調用即可)

能夠對本身進行覆蓋拷貝的函數(shù),其又同時兼?zhèn)淞?memcpy函數(shù) 可做的事


在這里插入圖片描述

//去除 sb 緩沖區(qū)左端的所有 空格,tab,'\t'。
void strbuf_ltrim(struct strbuf *sb)
{while((*(sb->buf)==' '||sb->buf[0]=='\t')&&sb->buf[0]!='\0')//這兩種都可以,就是不行sb->buf==' ',因為
    {                //C++ forbids comparison between pointer and integer
        sb->buf=(char*)memmove(sb->buf,sb->buf+1,sb->len-1);//全部向左平移一位
        sb->len--;
    }
}


//去除 sb 緩沖區(qū)右端的所有 空格,tab, '\t'。
void strbuf_rtrim(struct strbuf *sb)
{while((sb->buf[(sb->len)-1]==' '||sb->buf[(sb->len)-1]=='\t')&&sb->buf[(sb->len)-1]!='\0')
    {// sb->buf+(sb->len)-1='\0';錯
        // sb->buf=(char*)memmove(sb->buf+(sb->len)-1,sb->buf+(sb->len),1);
        sb->buf[(sb->len)-1]='\0';
        sb->len--;
    }
}


//刪除 sb 緩沖區(qū)從 pos 坐標長度為 len 的內容。
void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
{// int i;多此一舉
    // for(i=0;i//     sb->buf[pos+i]=0;
    // }
    memmove(sb->buf+pos,sb->buf+pos+len,sb->len-pos-len);//strlen(sb->buf+pos+len)+1  OK!
    sb->len=sb->len-len;
    sb->buf[sb->len]='\0';
}

在這里插入圖片描述

//sb 增長 hint ? hint : 8192 大小, 然后將文件描述符為 fd 的所有文件內容追加到 sb 中。
ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)//數(shù)據類型解釋見下
{ssize_t result; //根據函數(shù)返回值定數(shù)據類型
    sb->buf=(char*)realloc(sb->buf,sb->alloc+(hint ? hint : 8192));//不是sb->len+(...)
    sb->alloc+=(hint ? hint : 8192);
    
    result=read(fd,sb->buf+sb->len,sb->alloc-sb->len-1);//函數(shù)解釋見下
    if(result){sb->len+=result;
        sb->buf[sb->len]='\0';
    }
    return result;
}


//將 將文件句柄為 fp 的一行內容(拋棄換行符)讀取到 sb 。
int strbuf_getline(struct strbuf *sb, FILE *fp)
{char ch;
	// ch=fgetc(fp); //返回值為int,然后再ch!=EOF ->ok因為EOF=-1
	法一:
	while((ch = fgetc(fp))!='\n' && ch!=EOF)
	{		strbuf_addch(sb, ch);
	}
	法二:
	while((ch = fgetc(fp))!='\n' && feof(fp)==0)//順序不能錯,函數(shù)解釋見下
	{		strbuf_addch(sb, ch);
	}
    return 1;
}

read

ssize_t read(int fd ,void *buf, size_t count);
作用:從文件描述符對應的文件讀取數(shù)據,調用成功返回讀出的字節(jié)數(shù)
頭文件:include

  • fd:文件描述符
  • buf:讀出數(shù)據的緩沖區(qū)(將文件中讀出目標數(shù)據,放入buf緩沖區(qū)中)
  • count:每次讀取的字節(jié)數(shù)(是請求讀取的字節(jié)數(shù),讀上來的數(shù)據保存在緩沖區(qū)buf中,同時文件的當前讀寫位置向后移)
  • 返回值:成功返回讀出的字節(jié)數(shù);失敗返回-1;EOF返回0

ssize_t

有符號整型,在32位機器上等同與int,在64位機器上等同與long int

文件描述符

  • 操作系統(tǒng)利用文件描述符來訪問文件。
  • 文件描述符是非負整數(shù)。
  • 打開現(xiàn)存文件或新建文件時,內核會返回一個文件描述符。
  • 讀寫文件也需要使用文件描述符來指定待讀寫的文件。
  • 只要獲取到文件描述符,就可以找到對應的文件

文件句柄pf/fp

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
出錯返回NULL

fgetc

int fgetc(FILE *filename)

  • 在文件處理中,通過fgetc()函數(shù),我們從輸入流中獲取下一個字符,并將文件指針加1 (調用一次,filename向后移動一位)
  • 返回一個整數(shù)值,該值是無符號char的轉換。 它還返回EOF ,而EOF也是一個整數(shù)值-1 。

在這里插入圖片描述

feof

int feof(FILE *fp)

  • 當文件被打開,光標處于默認的開頭時,光標后都有信息,這時候調用feof()來查看光標后是否還有內容,就沒意義
  • 先使用getc(),從文件中讀取一個字符,讓光標向后移動一個字符。這時空文件的光標就已經移動到EOF的后面,這時使用feof()就會返回1了
  • 原理:站在光標所在位置,向后看看還有沒有字符。如果有,返回0;如果沒有,返回非0。它并不會讀取相關信息,只是查看光標后是否還有內容。

在這里插入圖片描述

//實現(xiàn)字符串切割
//將長度為 len 的字符串 str 根據切割字符 terminator 切成多個 strbuf,并從結果返回,
//max 可以用來限定大切割數(shù)量。返回 struct strbuf 的指針數(shù)組,數(shù)組的最后元素為 NULL
struct strbuf **strbuf_split_buf(const char *str, size_t len, int terminator, int max)
{//分配內存
    struct strbuf **p=(struct strbuf**)malloc(sizeof(struct strbuf*)*(max+1));//切3次,有3+1塊
                   //由于要返回struct strbuf的指針數(shù)組,因此定義**p,相當于*p[],
                   //即數(shù)組p里面每個單元都是一個struct strbuf*指針,單獨指向一個結構體(而每個結構體的buf指向一塊內存空間,所以下面需要兩次申請malloc)
//指針保存被切割字符串的首末地址
    const char *begin=str;
    const char *end=str+len;  // 字符串str \0的位置
    const char *next;
    int i=0;
    int len2;
//首元素是被切割字符的話,就直接跳過(p[]里放空內容沒有意義)
    while(*begin==terminator)
        begin++;
//從第一個非切割字符開始,往后遍歷整個str,找切割字符terminator
    for (next=begin;next<=end;next++)
    {if (*next==terminator||next==end)//注意是否要*
        {len2=next-begin;//地址之差,長度即為小塊的字符串
            p[i]=(struct strbuf*)malloc(sizeof(struct strbuf));//為指針p[i]所指向的結構體申請一塊內存,用于存放結構體(然后p[i]指向了這塊空間,也就指向了那個結構體)
            p[i]->len=len2;
            p[i]->alloc=len2+1;
            p[i]->buf=(char*)malloc(sizeof(char)*(len2+1));// 因為結構體里的buf為指針,又指向了一塊內存,所以向上面各個函數(shù)一樣,malloc?。ㄗ⒁鈂0)
            memcpy(p[i]->buf,begin,len2);//len2長度的限制,從而實現(xiàn)將一小塊字符串拷貝到buf中
            *(p[i]->buf+len2)='\0';
            i++;
           //連續(xù)的切割字符沒有意義,跳過
            while(*next==terminator&&next<=end)
                next++;
           //新的頭(每切一塊,頭就向后移動對應的長度)
            begin=next;
        }
        if(i==max)
            break;
    }
    p[i]=NULL;
    return p;
}


//實現(xiàn)判斷一個 strbuf 是否以指定字符串開頭的功能
//target_str:目標字符串,str:前綴字符串,strnlen:target_str 長度,前綴相同返回 true 失敗返回 false
bool strbuf_begin_judge(char* target_str, const char* str, int strnlen)//返回值解釋見下
{法一:
     if(strnlen==0 || memcmp(target_str,str,strlen(str))==0)//相同返回0,函數(shù)解釋見下
         return true;
     else 
         return false;

法二:
    int i;
    for(i=0;iif(str[i]=='\0')
            break;
        if(target_str[i]!=str[i])
            return false;
    }
    return true;
}


//獲取字符串從坐標 [begin, end) 的所有內容(可以分成引用和拷貝兩個模式)
//target_str:目標字符串,begin:開始下標,end:結束下標,len:target_buf的長度
//下標從0開始,[begin, end)區(qū)間,參數(shù)不合法返回 NULL
char* strbuf_get_mid_buf(char* target_buf, int begin, int end, int len)
{if(end

memcmp

int memcmp(const void *str1, const void *str2, size_t n)

  • str1 : 指向內存塊的指針
  • str2 : 指向內存塊的指針
  • n : 要被比較的字節(jié)數(shù)
  • 返回值 :當str1????當str1=str2時,返回值=0
    ????當str1>str2時,返回值>0

如果兩個字符串相同而且n大于字符串長度的話,memcmp不會在\0處停下來,會繼續(xù)比較\0后面的內存單元,直到_res不為零或者達到n的次數(shù)。

  • 但strncmp必定會在最短的字符串的末尾停下來,即使count還未為零

boll

頭文件:include
只能用來存放兩個值:true (1) 和 false (0)

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧

網頁名稱:設計一個C語言的動態(tài)擴容緩沖區(qū)-創(chuàng)新互聯(lián)
文章源于:http://www.muchs.cn/article38/hgdsp.html

成都網站建設公司_創(chuàng)新互聯(lián),為您提供品牌網站設計定制網站、域名注冊、品牌網站建設網站排名、標簽優(yōu)化

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)