一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

淺析C語言中關(guān)于字符串的操作 - C/C++ - 課堂 - 話題 - 張劍 - CSDN學(xué)...

 Eva.zhou 2011-01-31


 

淺析C語言中關(guān)于字符串的操作
作者:杭州書誠(chéng)
前言:如果您是學(xué)C/C++的,對(duì)于字符串的操作不是很了解,請(qǐng)您耐心讀完。作為我的朋友,我很樂意和您分享我最近的知識(shí)積累。畢竟,網(wǎng)上很少有這么全,這么細(xì)的介紹,更少有人愿意花時(shí)間收集N個(gè)相關(guān)帖子,讀懂,并將零散的知識(shí)點(diǎn)整理,并思考函數(shù)之間可能的聯(lián)系或改進(jìn)方法。如果您覺得不錯(cuò),請(qǐng)您分享,您的支持就是給我繼續(xù)整理的動(dòng)力。
一.字符串相關(guān)操作分類介紹(招式篇)
1.字符串拷貝相關(guān)操作。
a. strcpy:
char *strcpy (char *dest, const char *src);
復(fù)制字符串src到dest中。返回指針為dest的值。
b. strncpy:
char *strncpy (char *dest, const char *src, size_t n);
復(fù)制字符串src到dest中,最多復(fù)制n個(gè)字符。返回指針為dest的值。
c. strdup:
char *strdup (const char *s);
得到一個(gè)字符串s的復(fù)制。返回指針指向復(fù)制后的字符串的首地址。
d. memcpy:
void *memcpy (void *dest, const void *src, size_t n);
從src所指向的對(duì)象復(fù)制n個(gè)字符到dest所指向的對(duì)象中。返回指針為dest的值。
e .mem**y:
void *mem**y (void *dest, const void *src, int c, size_t n);
從src所指向的對(duì)象復(fù)制n個(gè)字符到dest所指向的對(duì)象中。如果復(fù)制過程中遇到了字符c則停止復(fù)制,返回指針指向dest中字符c的下一個(gè)位置;否則返回NULL。
f. memmove:
void *memmove (void *dest, const void *src, size_t n);
從src所指向的對(duì)象復(fù)制n個(gè)字符到dest所指向的對(duì)象中。返回指針為dest的值。不會(huì)發(fā)生內(nèi)存重疊。
2.字符串比較相關(guān)操作
a. strcmp:
int strcmp (const char *s1, const char *s2);
比較字符串s1和字符串s2。返回值是s1與s2第一個(gè)不同的字符差值的符號(hào),0表示相同,1表示正號(hào),-1表示負(fù)號(hào)。
b. strncmp:
int strncmp (const char *s1, const char *s2, size_t n);
比較字符串s1和字符串s2,最多比較n個(gè)字符。返回值是s1與s2第一個(gè)不同的字符差值的符號(hào),0:表示相同,1:表示正號(hào),-1:表示負(fù)號(hào)。
c. stricmp:
int stricmp (const char *s1, const char *s2);
比較字符串s1和字符串s2,忽略大小寫。返回值是s1與s2第一個(gè)不同的字符差值的符號(hào),0:表示相同,1:表示正號(hào),-1:表示負(fù)號(hào)。
d. strnicmp:
int strnicmp(const char *s1, const char *s2, size_t n);
比較字符串s1和字符串s2,忽略大小寫,最多比較n個(gè)字符。返回值是s1與s2第一個(gè)不同的字符差值。
e. memcmp:
int memcmp (const void *s1, const void *s2, size_t n);
比較s1所指向的對(duì)象和s2所指向的對(duì)象的前n個(gè)字符。返回值是s1與s2第一個(gè)不同的字符差值。
f. memicmp:
int memicmp (const void *s1, const void *s2, size_t n);
比較s1所指向的對(duì)象和s2所指向的對(duì)象的前n個(gè)字符,忽略大小寫。返回值是s1與s2第一個(gè)不同的字符差值的符號(hào),0:表示相同,1:表示正號(hào),-1:表示負(fù)號(hào)。
3.字符串大小寫轉(zhuǎn)換相關(guān)操作
a. strlwr:
char *strlwr (char *s);
將字符串s全部轉(zhuǎn)換成小寫。返回指針為s的值。
b. strupr:
char *strupr (char *s);
將字符串s全部轉(zhuǎn)換成大寫。返回指針為s的值。
4.字符串連接相關(guān)操作
a. strcat:
char *strcat (char *dest, const char *src);
將字符串src添加到dest尾部。返回指針為dest的值。
b. strncat:
char *strncat (char *dest, const char *src, size_t n);
將字符串src添加到dest尾部,最多添加n個(gè)字符。返回指針為dest的值。
5.字符串子串相關(guān)操作
a. strstr:
char *strstr (const char *s1, const char *s2);
在字符串s1中搜索字符串s2。如果搜索到,返回指針指向字符串s2第一次出現(xiàn)的位置;否則返回NULL。
b. strcspn:
size_t strcspn (const char *s1, const char *s2);
返回值是字符串s1的完全由不包含在字符串s2中的字符組成的初始串長(zhǎng)度。
c. strspn:
    size_t strspn (const char *s1, const char *s2);
返回值是字符串s1的完全由包含在字符串s2中的字符組成的初始串長(zhǎng)度。
d. strpbrk:
char *strpbrk (const char *s1, const char *s2);
返回指針指向字符串s1中字符串s2的任意字符第一次出現(xiàn)的位置;如果未出現(xiàn)返回NULL。
e. strtok:
char *strtok (char *s1, const char *s2);
用字符串s2中的字符做分隔符將字符串s1分割。返回指針指向分割后的字符串。第一次調(diào)用后需用NULLL替代s1作為第一個(gè)參數(shù)。
6.字符串與單個(gè)字符相關(guān)操作
a. strchr:
char *strchr (const char *s, int c);
在字符串s中搜索字符c。如果搜索到,返回指針指向字符c第一次出現(xiàn)的位置;否則返回NULL。
b. strrchr:
char *strrchr (const char *s, int c);
在字符串s中搜索字符c。如果搜索到,返回指針指向字符c最后一次出現(xiàn)的位置;否則返回NULL。
c. memchr:
void * memchr (const void *s, int c, size_t n);
在s所指向的對(duì)象的前n個(gè)字符中搜索字符c。如果搜索到,返回指針指向字符c第一次出現(xiàn)的位置;否則返回NULL。
d. memset:
void *memset (void *s, int c, size_t n);
設(shè)置s所指向的對(duì)象的前n個(gè)字符為字符c。返回指針為s的值。
e. strnset:
char *strnset (char *s, int ch, size_t n);
設(shè)置字符串s中的前n個(gè)字符全為字符c。返回指針為s的值。
f. strset:
char *strset (char *s, int ch);
設(shè)置字符串s中的字符全為字符c。返回指針為s的值。
7..字符串求字符串長(zhǎng)度相關(guān)操作
a. strlen:
size_t strlen (const char *s);
返回值是字符串s的長(zhǎng)度。不包括結(jié)束符\0。
8..字符串錯(cuò)誤相關(guān)操作
a. strerror:
char *strerror(int errnum);
返回指針指向由errnum所關(guān)聯(lián)的出錯(cuò)消息字符串的首地址。errnum的宏定義見errno.h。
9..字符串反置操作
a. strrev:
char *strrev (char *s);
將字符串全部翻轉(zhuǎn),返回指針指向翻轉(zhuǎn)后的字符串。
 
二.記憶方法(記憶心法精要)
    最好的方法莫過于多用了,但是具體函數(shù)的名字的記憶也是有技巧的。
str : 字符串      cmp : 比較      n : 代表范圍     i : 表示不區(qū)分大小寫
rev : 翻轉(zhuǎn)        error : 錯(cuò)誤      len : 長(zhǎng)度        mem :內(nèi)存
cat : 連接        lwr : 小寫        upr:大寫
set : 設(shè)置(單個(gè)字符)          chr : 單個(gè)字符(查找)
注:
1.只要是關(guān)于內(nèi)存操作(mem…)的,則必有一個(gè)參數(shù)size_t n,表示對(duì)操作內(nèi)存的大小。這與strn…函數(shù)在功能上類似,只是,strn…函數(shù)一般在沒達(dá)到n個(gè)字節(jié)之前遇到空字符時(shí)會(huì)結(jié)束,而mem…遇到空字符并不結(jié)束。
2.str/mem +[n] chr : 表示在字符串中查找某個(gè)字符,而str/mem +[n] set : 表示把字符串設(shè)置成某個(gè)字符,n表示作用范圍為前n個(gè)。
3.strdup中返回的指針是new出來的,用完一定要記得delete。
 
三.源碼 && 解析 && BUG:(內(nèi)功篇)
1.字符串拷貝相關(guān)操作。
a. strcpy: 復(fù)制字符串src到dest中。返回指針為dest的值。
char *strcpy(char * dest, const char * src)
{
      assert( (dest!=NULL)&&( src!=NULL) );
      char *p = dest;
      while(*p ++=* src ++);
      return (dest);
}
解析:
1.assert:斷言,即assert(),()里的一定為真,否則程序報(bào)錯(cuò)。
2.*src++中,++后置的優(yōu)先級(jí)比*高,所以相當(dāng)于*(src++),src++的意思是,先取出src的值,再++,所以*src++;等同于:*src;src++;
3.將src指針?biāo)傅刂分兴凶址紡?fù)制到dest指針?biāo)傅刂分?,直至遇到空字符才結(jié)束。并且dest和返回指針都指向拷貝后地址空間的首地址。
BUG:
沒有對(duì)src指針?biāo)傅刂房臻g的大小進(jìn)行檢查(實(shí)際是無法進(jìn)行檢查),所以,當(dāng)所需要拷貝的字符串超出src指針?biāo)傅刂房臻g的大小時(shí),內(nèi)存出錯(cuò)。
案例:
#include"iostream"
using namespace std;
void strcpyTest0()
{
int i;
    char *szBuf = new char[128];
   for(i=0;i<128;i++)
        szBuf[i]=''*'';
   szBuf[127]=''\0'';     //構(gòu)造一個(gè)全部是*的字符串
    char szBuf2[256];
for(i=0;i<256;i++)
szBuf2[i]=''#'';
    szBuf2[255]=''\0'';   //構(gòu)造一個(gè)全部是#的字符串
    strcpy(szBuf,szBuf2);
   cout<<szBuf<<endl;
}
int main(void)
{
    strcpyTest0();
    return 0;
}
結(jié)果:在此程序中,雖然輸出了255個(gè)#,但是卻彈出了內(nèi)存錯(cuò)誤,這個(gè)原因很好解釋,因?yàn)閟zBuf本身只有128個(gè)字節(jié),可卻給它拷貝了256個(gè)字節(jié)的內(nèi)容。從其源碼可以看出,strcpy將會(huì)把szBuf2中的所有內(nèi)容全部考到szBuf中,直至遇到了空字符。所以,切忌,在用strcpy函數(shù)時(shí)要保證szBuf有足夠的內(nèi)存空間,否則會(huì)出錯(cuò)。
猜測(cè)性改進(jìn)方法1:
char *strcpy(char dest[], const char src[])
{
assert( (dest!=NULL)&&( src!=NULL) );
    char *p = dest;
    int Length = strlen (dest);
    while(*p++=*src++ &&Length--) {}
       * (p – 1) =  ''\0'';
    return (dest);
}
這樣就可以使拷貝到dest中的數(shù)不會(huì)超過dest所擁有的空間大小了。經(jīng)調(diào)試,確實(shí)不會(huì)超過dest所指大小,也就沒有了內(nèi)存錯(cuò)誤。但是,strlen只是求得該字符串的長(zhǎng)度遇到''\0''就結(jié)束了,假如上述案例中szBuf[127]=''\0'';改為szBuf[10]=''\0'';則szBuf中只能拷貝10個(gè)字符,再加一個(gè)''\0''結(jié)尾,可szBuf明明是有127個(gè)字符空間的,所以此法不行。
猜測(cè)性改進(jìn)方法2:把dest所能存儲(chǔ)的字符長(zhǎng)度作為參數(shù)傳進(jìn)去,這樣就產(chǎn)生了strncpy函數(shù),具體請(qǐng)見strncpy函數(shù)。
猜測(cè)性改進(jìn)方法3:僅僅傳入需要拷貝的地址,然后再new出一塊內(nèi)存存放拷貝的字符串,再返回new出來的內(nèi)存的首地址,最后由調(diào)用函數(shù)delete,這樣就產(chǎn)生了strdup函數(shù),具體請(qǐng)見strdup函數(shù)。
 
b. strncpy: 復(fù)制字符串src到dest中,最多復(fù)制n個(gè)字符。返回指針為dest的值。
char *strncpy (char *dest, const char *src, size_t n)
{
assert( (dest!=NULL)&&( src!=NULL) );
char *p = dest;
     while (n && (*p++ = *src++))
n --;
while(n --)
*p++ = ''\0'';//遇空字符結(jié)束后,將其后所有字符付為空字符
return(dest);
}
解析:
1.若src所指地址空間的字符串個(gè)數(shù)<n,則將src所指地址空間的字符串附給dest后,再將其后的n-strlen(src)附為空字符。
2. dest和返回指針都指向拷貝后地址空間的首地址。
BUG:
當(dāng)n比源字符串空間要小的時(shí)候,strncpy并沒有用”\0”來結(jié)束字符串。從上述源碼可以看出當(dāng)n =0時(shí),是不會(huì)執(zhí)行while(n--)  *p++ = ''\0'';的,這樣,如果在前面并沒付”\0”來結(jié)束字符串,則后面也不會(huì)再付”\0”來結(jié)束字符串。
案例:
#include"iostream"
using namespace std;
void strcpyTest1()
{
         int i;
         char szBuf[128];
          for(i=0;i<128;i++)
szBuf[i]=''*'';
          szBuf[127]=''\0'';
          char szBuf2[256];
         for(i=0;i<256;i++)
szBuf2[i]=''#'';
          szBuf2[255]=''\0'';
         strncpy(szBuf,szBuf2,10);
          cout<<szBuf<<endl;
}
int main(void)
{
        strcpyTest1();
        return 0;
}
結(jié)果可不是:##########,而是:##########*********************************************************************************************************************
改進(jìn)方法:
char *strncpy(char * dest, const char * src, size_t n)
{
assert( (dest!=NULL)&&( src!=NULL) );
char *p = dest;
           while (n && (*p++ = *src++))
n--;
           while(n--)
                  *p++ = ''\0'';//遇空字符結(jié)束后,將其后所有字符付為空字符
*p =''\0'';
            return(dest);
}
注:覺得這個(gè)BUG本可以避免的,這絕對(duì)是他們故意的。
 
c. strdup: 得到一個(gè)字符串str的復(fù)制。返回指針指向復(fù)制后的字符串的首地址。
char *strdup (const char *str);
{
       char *p;
           if (!str)
                 return(NULL);
           if (p = malloc(strlen(str) + 1))
                  return(strcpy(p,str));
           return(NULL);
}
解析:
1.沒有assert語句,允許str為NULL
2. str為NULL,返回NULL,否則返回指向復(fù)制后的字符串的首地址。
BUG:
    在strdup中申請(qǐng)的內(nèi)存空間,必須要在調(diào)用函數(shù)中釋放,這樣就使內(nèi)存的申請(qǐng)和釋放分開,用戶很容易忘記了主動(dòng)施放內(nèi)存,會(huì)導(dǎo)致內(nèi)存泄漏。
改進(jìn)方法:
通過命名方式提醒用戶主動(dòng)釋放內(nèi)存:如在函數(shù)名之前加
ndp_   :   need delete pointer 需要主動(dòng)施放指針
nfp_   :   need free pointer    需要主動(dòng)施放指針
ndpa_   :   need delete pointer array   需要主動(dòng)施放指針數(shù)組
nfpa_   :   need delete pointer array   需要主動(dòng)施放指針數(shù)組
思想借鑒:
鑒于這種思想,在傳遞參數(shù)時(shí),如果需要傳出的參數(shù)的空間大小在調(diào)用此函數(shù)前是不知道的,又不想浪費(fèi)空間,則可以考慮new — delete。如果再用到上述的命名方法,則將提醒用戶,不至于使用戶忘記釋放內(nèi)存。這樣可能會(huì)使部分函數(shù)的名字很難記憶,那么如果把ndp_改為_ndp加在函數(shù)名之后,則可借助VC assist輕松獲得該函數(shù)。
我有一種想法,只要大家都能理解這種思想,并且這么做,這種動(dòng)態(tài)開辟內(nèi)存的方法也許有可能會(huì)改變我們現(xiàn)有的編程模式。
我現(xiàn)在還不是很明白動(dòng)態(tài)開辟和釋放在時(shí)間性能上的弱點(diǎn),也許這種方法會(huì)給程序帶來災(zāi)難,希望知道的人能夠告訴我,這種方式在帶來空間上的不浪費(fèi)的優(yōu)點(diǎn)的同時(shí)會(huì)有哪些潛在的危險(xiǎn)和不足。
 
d. memcpy: 從src所指向的對(duì)象復(fù)制n個(gè)字符到dest所指向的對(duì)象中。返回指針為dest的值。
void *memcpy (void *dest, const void *src, size_t n)
{
     assert((dest!=NULL)&&( src!=NULL));
          void * p = dest;
       while (n --)
          {
                   *(char *)p = *(char *)src;
                p = (char *)dest + 1;
                    src = (char *)src + 1;
        }
         return(dest);
}
解析:
1. 精妙之處在于指針之間的轉(zhuǎn)換。傳進(jìn)來的參數(shù)是兩個(gè)void類型的指針,均指向內(nèi)存中的某一塊地址,而這個(gè)地址本身都是四個(gè)字節(jié)的,不管是什么類型的地址都是四個(gè)字節(jié),那么只要經(jīng)過強(qiáng)制轉(zhuǎn)化,則void型指針可以轉(zhuǎn)化為任意類型的指針。(類型名)指針名,當(dāng)然它傳出的也是void型的指針,同理也可以轉(zhuǎn)化成任意類型的,那么這個(gè)函數(shù)的功能可就不僅僅是拷貝字符串了,任何存在類存的東西都能考的。具體見下案例。
2.dest和返回指針都指向拷貝后內(nèi)存空間的首地址。
案例:
#include"iostream"
using namespace std;
void strcpyTest1()
{
          int A=1;
         int B=0xffffffff;
         memcpy(&A,&B,1);
         cout<<A<<endl;
}
int main(void)
{
       strcpyTest1();
         return 0;
}
結(jié)果:將會(huì)輸出255,為什么不是0xffffffff呢,因?yàn)槟阒豢截惲艘粋€(gè)字節(jié),而int型的是4個(gè)字節(jié),所以,你只要將memcpy(&A,&B,1);改為memcpy(&A,&B,4);就把B的值付給了A。當(dāng)然是-1了。啊,不明白為什么?原碼,補(bǔ)碼,移碼,還有有符號(hào)數(shù),無符號(hào)數(shù)總知道了吧,還不知道,那只好去看看唐碩飛老師的組成原理了,就是考驗(yàn)推薦的參考書之一。但同時(shí)也要注意,n的值不能超過A和B的范圍,不然要么就是得到的數(shù)據(jù)是不合法的,要么就是存儲(chǔ)不合法。
至于越界了會(huì)不會(huì)崩掉,那還要拼人品,如果你復(fù)制的越界內(nèi)存已經(jīng)分配出去了,將會(huì)出現(xiàn)內(nèi)存錯(cuò)誤,如果沒有,則可繼續(xù)運(yùn)行,但如果編譯器會(huì)強(qiáng)制規(guī)定對(duì)于某個(gè)變量的操作不能超出他的內(nèi)存范圍,則可能不能通過運(yùn)行。具體目前我也不是很清楚,還望知情者點(diǎn)撥一下。
 
e .mem**y:從src所指向的對(duì)象復(fù)制n個(gè)字符到dest所指向的對(duì)象中。如果復(fù)制過程中遇到了字符c則停止復(fù)制,返回指針指向dest中字符c的下一個(gè)位置;否則返回NULL。
void * mem**y(void *dest,const void *src,int c, size_t n)
{
         while (n && (*((char *)(dest = (char *)dest + 1) - 1) =
  *((char *)(src = (char *)src + 1) - 1)) != (char)c )
                           n--;
        return(n ? dest : NULL);
}
解析:
1.模擬循環(huán)體執(zhí)行:
dest = (char *)dest + 1;
src = (char *)src + 1;
(char *) (dest-1)= (char *) (src -1);
n && (*((char *) (dest-1)) != (char)c);
2.如此寫法,不但難懂,且易錯(cuò),最主要的是代碼行數(shù)太少,影響工資,呵呵……
 
f. memmove:從src所指向的對(duì)象復(fù)制n個(gè)字符到dest所指向的對(duì)象中。返回指針為dest的值。不會(huì)發(fā)生內(nèi)存重疊。
void *memmove (void *dest, const void *src, size_t n);
{  
    void * ret = dst;  
    if (dst <= src || (char *)dst >= ((char *)src + n))
    {      
        while (n--)            
        {          
            *(char *)dst = *(char *)src;           
            dst = (char *)dst + 1;         
            src = (char *)src + 1;         
        }      
    }  
    else       
    {      
        dst = (char *)dst + n - 1;     
        src = (char *)src + n - 1;
        while (n--)            
        {          
            *(char *)dst = *(char *)src;           
            dst = (char *)dst - 1;         
            src = (char *)src - 1;         
        }
    }  
    return(ret);   
}
解析:
    1.當(dāng)目的地址在源地址之后,且目的地址和源地址之間有重疊區(qū)域時(shí),將從最后一個(gè)字符開始,將源地址區(qū)域字符串付給目的地址,否則從第一個(gè)字符開始將源地址區(qū)域字符串付給目的地址。這樣就能保證即使在源/目的地址區(qū)域有重疊的情況下也能正確的復(fù)制內(nèi)存。
 
BUG總結(jié)(精要):
1.copy函數(shù)要注意copy后目的地址空間不能越界,有的是用參數(shù)控制,有的并不控制,有的內(nèi)存立馬出錯(cuò),有的概率性報(bào)錯(cuò),cat 函數(shù)也要注意這些??傊饋砭褪且痪湓挘残薷膬?nèi)存內(nèi)容的,要注意不能越界,字符串處理操作除了受參數(shù) size_t n來控制范圍外,遇到空字符結(jié)束,而內(nèi)存處理操作只受參數(shù)size_t n控制。
2.mem 函數(shù)都涉及到內(nèi)存空間大小,操作時(shí)一定要注意空間別越界,越界了報(bào)錯(cuò)是概率性的,可能你那運(yùn)行沒錯(cuò),客戶一運(yùn)行就錯(cuò)了。memcpy對(duì)于目的地址在源地址之后,且目的地址和源地址之間有重疊區(qū)域這種情況下的拷貝是不正確的,可以用memmove來代替。
3.strncpy有一個(gè)結(jié)尾符不是空字符的BUG,也許人家就是這么設(shè)計(jì)的,但你要記得n<strlen(src)時(shí),n個(gè)復(fù)制字符之后,并不以空字符結(jié)尾。
 
2.字符串比較相關(guān)操作
a. strcmp: 比較字符串s1和字符串s2。返回值是s1與s2第一個(gè)不同的字符差值的符號(hào),0:表示相同,1:表示正號(hào),-1:表示負(fù)號(hào)。
int strcmp (const char *s1, const char *s2)
{
     assert((s1 != NULL) && (s2!= NULL));
         int ret = 0 ;
         while( ! (ret = *( unsigned char *) s1- *(unsigned char *) s2) && * s2)
                  s1++, s2++;
         if ( ret < 0 )
               ret = -1 ;
         else if ( ret > 0 )
               ret = 1 ;
         return(ret);
}
解析:
1.逗號(hào)語句將本來的兩個(gè)語句合為一句,省去了while循環(huán)的{},精簡(jiǎn)代碼行數(shù)。
2.只涉及到讀取內(nèi)存,而不修改內(nèi)存,故而只要傳入的參數(shù)本身沒有問題,則不會(huì)出現(xiàn)現(xiàn)隱藏BUG。(我是這么覺得的)
 
b. strncmp:比較字符串s1和字符串s2,最多比較n個(gè)字符。返回值是s1與s2第一個(gè)不同的字符差值的符號(hào),0:表示相同,1:表示正號(hào),-1:表示負(fù)號(hào)。
int strncmp(const char *s1,const char *s2, size_t n)
{
    assert((s1 != NULL) && (s2!= NULL));
    if (!n)
        return(0);
    while (--n && *s1 && *s1 == *s2)
        s1++,s2++;
    if (*(unsigned char *)s1 - *(unsigned char *)s2 > 0)
            return 1;
    else if (*(unsigned char *)s1 - *(unsigned char *)s2 <0)
            return -1;
    else
            return 0;  
}
解析:略
 
c. stricmp:比較字符串s1和字符串s2,忽略大小寫。返回值是s1與s2第一個(gè)不同的字符差值的符號(hào),0:表示相同,1:表示正號(hào),-1:表示負(fù)號(hào)。
int stricmp1(const char *s1, const char *s2)
{
    assert((s1 != NULL) && (s2!= NULL));
    int ch1, ch2;
    do
    {
        if ( ((ch1 = (unsigned char)(*(s1++))) >= ''A'') &&(ch1 <= ''Z'') )
            ch1 += 0x20;
        if ( ((ch2 = (unsigned char)(*(s2++))) >= ''A'') &&(ch2 <= ''Z'') )
            ch2 += 0x20;
    } while ( ch1 && (ch1 == ch2) );
    if (*(unsigned char *)s1 - *(unsigned char *)s2 > 0)
        return 1;
    else if (*(unsigned char *)s1 - *(unsigned char *)s2 <0)
        return -1;
    else
        return 0;
}
解析:略
 
d. strnicmp:比較字符串s1和字符串s2,忽略大小寫,最多比較n個(gè)字符。返回值是s1與s2第一個(gè)不同的字符差值的符號(hào),0:表示相同,1:表示正號(hào),-1:表示負(fù)號(hào)。
int strnicmp(const char *s1,const char *s2,size_t n)
{
    int ch1, ch2;
    do
    {
        if ( ((ch1 = (unsigned char)(*(s1++))) >= ''A'') &&(ch1 <= ''Z'') )
            ch1 += 0x20;
        if ( ((ch2 = (unsigned char)(*(s2++))) >= ''A'') &&(ch2 <= ''Z'') )
            ch2 += 0x20;
    } while ( --n && ch1 && (ch1 == ch2) );
    if (*(unsigned char *)s1 - *(unsigned char *)s2 > 0)
        return 1;
    else if (*(unsigned char *)s1 - *(unsigned char *)s2 <0)
        return -1;
    else
        return 0;
}
解析:略
 
e. memcmp: 比較buffer1所指向的對(duì)象和buffer2所指向的對(duì)象的前n個(gè)字符。返回值是buffer1與buffer2第一個(gè)不同的字符差值的符號(hào),0:表示相同,1:表示正號(hào),-1:表示負(fù)號(hào)。
int memcmp(const void *buffer1,const void *buffer2,size_t n)
{
    if (!n)
        return(0);
    while ( --n && *(char *)buffer1 == *(char *)buffer2)
    {
        buffer1 = (char *)buffer1 + 1;
        buffer2 = (char *)buffer2 + 1;
    }
    if (*(unsigned char *)buffer1 - *(unsigned char *)buffer2 > 0)
        return 1;
    else if (*(unsigned char *)buffer1 - *(unsigned char *)buffer2 <0)
        return -1;
    else
        return 0;  
}
解析:略
 
f. memicmp: 比較s1所指向的對(duì)象和s2所指向的對(duì)象的前n個(gè)字符,忽略大小寫。返回值是buffer1與buffer2第一個(gè)不同的字符差值的符號(hào),0:表示相同,1:表示正號(hào),-1:表示負(fù)號(hào)。
int memicmp(const char *buffer1,const char *buffer2, size_t n)
{
    int ch1, ch2;
    do
    {
        if ( ((ch1 = (unsigned char)(*(buffer1++))) >= ''A'') &&(ch1 <= ''Z'') )
            ch1 += 0x20;
        if ( ((ch2 = (unsigned char)(*(buffer2++))) >= ''A'') &&(ch2 <= ''Z'') )
            ch2 += 0x20;
    } while ( --n && (ch1 == ch2) );
    if (*(unsigned char *)(buffer1-1) - *(unsigned char *)(buffer2-1) > 0)
        return 1;
    else if (*(unsigned char *)(buffer1-1) - *(unsigned char *)(buffer2-1) <0)
        return -1;
    else
        return 0;  
}
解析:略
 
注:字符串比較相關(guān)操作只涉及到內(nèi)存的讀取,而不修改內(nèi)存,所以,不會(huì)導(dǎo)致嚴(yán)重的不可預(yù)測(cè)的不良結(jié)果,源代碼已經(jīng)很清楚的展現(xiàn)了這些函數(shù)的功能,我也不愿再多費(fèi)唇舌。我曾在網(wǎng)上看到過有人說字符串比較相關(guān)操作的返回值是受比較的字符串的第一個(gè)不相同的字符之差,只是我用的VC6.0編譯器是其之差的符號(hào),但具體可能因編譯器而定,為了代碼擁有更好的可復(fù)制性,建議關(guān)于其返回的比較最好為 return_value > 0, return_value<0 和return_value == 0 ,不要寫成1 == return_value,-1 == return_value,可能有潛在的危險(xiǎn)。
 
三.字符串大小寫轉(zhuǎn)換相關(guān)操作
a. strlwr:將字符串str全部轉(zhuǎn)換成小寫。返回指針為str的值。
char * strlwr(char *str)
{
        char *p = str;
        while (*p != ''\0'')
        {
            if(*p >= ''A'' && *p <= ''Z'')
                *p = (*p) + 0x20;
            p++;
       }
        return str;
}
b. strupr:將字符串str全部轉(zhuǎn)換成大寫。返回指針為str的值。
char * strupr(char *str)
{
        char *p = str;
        while (*p != ''\0'')
        {
            if(*p >= ''a'' && *p <= ''z'')
                *p -= 0x20;
            p++;
        }
        return str;
}
注:雖然修改了內(nèi)存,但必在其字符串范圍之內(nèi),當(dāng)然也可以設(shè)置BUG,就是傳進(jìn)去需要大小寫轉(zhuǎn)換的字符串并沒有以空字符結(jié)束,那么可以惡意的修改內(nèi)存了,只是,一般沒人不會(huì)這么無聊的讓自己的程序崩掉。
四.字符串連接相關(guān)操作
a. strcat: 將字符串src添加到dest尾部。返回指針為dest的值。
char * strcat1(char * dest,char * src)
{
        char * p = dest + strlen(dest);
        strcpy(p,src);
        return dest;
}
 
b. strncat: 將字符串src添加到dest尾部,最多添加n個(gè)字符。返回指針為dest的值。
char * strncat1(char *dest,const char *src,size_t n)
{
        char *p = dest + strlen(dest); 
        while (n-- && (*p++ = *src++));
            *p = ''\0'';
        return(dest);
}
注:連接字符串時(shí)也要注意是否越界,分析可以參考拷貝類相關(guān)操作。用的時(shí)候多注意下,不至于導(dǎo)致很恐怖的事。
 
五.字符串子串相關(guān)操作
a. strstr: 在字符串s1中搜索字符串s2。如果搜索到,返回指針指向字符串s2第一次出現(xiàn)的位置;否則返回NULL。
char * strstr(const char *s1,const char *s2)
{
        assert((s1!=NULL) && (s2!=NULL));
        if (*s1 == 0)
        {
            if (*s2)
                return (char *) NULL;
            return (char *) s1;
        }
        while (*s1)
        {
            size_t i = 0;
            while (1)
            {
                if (s2[i] == 0)
                    return (char *) s1;
                if (s2[i] != s1[i])
                    break;
                i++;
            }
            s1++;
        }
        return (char *) NULL;
}
b. strcspn: 返回字符串s1的完全由不包含在字符串s2中的字符組成的初始串長(zhǎng)度。即從s1中第一個(gè)字符開始,按從前到后的順序到第一個(gè)屬于s2中的字符之間的字符個(gè)數(shù)。
size_t strcspn1(const char *s1 ,const char *s2)
{
    assert((s1 != NULL) && (s2 != NULL));
    const char *s = s1;
    const char *p;
    while (*s1)
    {
        for (p = s2; *p; p++)
        {
            if (*s1 == *p)
                break;
        }
        if (*p)
            break;
        s1++;
    }
    return s1 - s;
}
c. strspn:返回字符串s1的完全由包含在字符串s2中的字符組成的初始串長(zhǎng)度。即從s1中第一個(gè)字符開始,按從前到后的順序到第一個(gè)不屬于s2中的字符之間的字符個(gè)數(shù)。
    size_t strspn1(const char *s1 ,const char *s2)
{
assert((s1 != NULL) && (s2 != NULL));
        const char *s = s1;
        const char *p;
        while (*s1)
        {
            for (p = s2; *p; p++)
            {
                if (*s1 == *p)
                    break;
            }
            if (*p == ''\0'')
                break;
            s1++;
        }
        return s1 - s;
}
d. strpbrk:返回指針指向字符串s1中字符串s2的任意字符第一次出現(xiàn)的位置;如果未出現(xiàn)返回NULL。
char * strpbrk1(const  char *s1 ,const  char *s2)
{
        assert((s1!=NULL) && (s2!=NULL));
        const  char *c = s2;
        if (!*s1)
            return (char *) NULL;
        while (*s1)
        {
            for (c = s2; *c; c++)
            {
                if (*s1 == *c)
                    break;
            }
            if (*c)
                break;
            s1++;
        }
        if (*c == ''\0'')
            s1 = NULL;
        return (char *) s1;
}
e. strtok: 用字符串s2中的字符做分隔符將字符串s1分割。返回指針指向分割后的字符串。第一次調(diào)用后需用NULLL替代s1作為第一個(gè)參數(shù)。
使用案例:
#include"iostream"
using namespace std;
char string1[] = "A string\tof ,,tokens\nand some more tokens";
char seps[]   = " , \t\n";
char *token;
void main(void)
{
    cout<<"Tokens: "<<string1;
    token = strtok( string1, seps );   /* Establish string and get the first token: */
    while( token != NULL )
    {
        cout<<" token: "<< token <<endl;   /* While there are tokens in "string" */
        token = strtok( NULL, seps );   /* Get next token: */
    }
}
輸出結(jié)果:
Tokens: A string        of ,,tokens
            and some more tokens token: A
token: string
token: of
token: tokens
token: and
token: some
token: more
token: tokens
請(qǐng)按任意鍵繼續(xù). . .
 
六.字符串與單個(gè)字符相關(guān)操作
a. strchr: 在字符串str中搜索字符c。如果搜索到,返回指針指向字符c第一次出現(xiàn)的位置;否則返回NULL。
char * strchr(const  char *str, int c)
{
         while (*str && *str != (char)c)
                   str++;
         if (*str == (char)c)
               return((char *)str);
         return(NULL);
}
 
b. strrchr: 在字符串str中搜索字符c。如果搜索到,返回指針指向字符c最后一次出現(xiàn)的位置;否則返回NULL。
char * strrchr(const  char * str,int c)
{
        char *p = (char *)str;
         while (*str)
str++;
        while (str-- != p && *str != (char)c);
        if (*str == (char)c)
                  return( (char *)str );
        return(NULL);
}
 
c. memchr: 在buffer所指向的對(duì)象的前n個(gè)字符中搜索字符c。如果搜索到,返回指針指向字符c第一次出現(xiàn)的位置;否則返回NULL。
void * memchr(const void * buffer,int c, size_t n)
{
         while ( n && (*(unsigned char *)buffer != (unsigned char)c) )
         {
                  buffer = (unsigned char *)buffer + 1;
               n--;
         }
         return(n ? (void *)buffer : NULL);
}
 
d. memset: 設(shè)置buffer所指向的對(duì)象的前n個(gè)字符為字符c。返回指針為s的值。
void * memset(void * buffer,int c,int n)
{
         void *p = buffer;
         while (n--)
         {
              *(char *) buffer = (char)c;
                   buffer = (char *) buffer + 1;
         }
        return p;
}
 
e. strnset: 設(shè)置字符串str中的前n個(gè)字符全為字符c。返回指針為s的值。
char * strnset(char * str,int c, size_t n)
{
         char *p = str;
         while (n-- && *p)
              *p++ = (char)c;
         return(p);
}
f. strset: 設(shè)置字符串str中的字符全為字符c。返回指針為s的值。
char * strset(char *str,int c)
{
         char *p = str;
         while (*str)
                  *str++ = (char)c;
         return(p);
}
 
七.字符串求字符串長(zhǎng)度相關(guān)操作size_t
a. strlen: 返回值是字符串str的長(zhǎng)度。不包括結(jié)束符\0。
size_t strlen (const char * str )
{
          const char *p = str;
          while( *p++ ) ;
             return( (int)(p - str - 1) );
}
注:字符串必須以空字符結(jié)束。strlen往往與sizeof混淆,其實(shí)這是兩個(gè)完全不同的概念,sizeof是返回一個(gè)代號(hào)所具有的內(nèi)存空間,跟你里面存放的是什么一點(diǎn)關(guān)系都沒有。
八.字符串錯(cuò)誤相關(guān)操作
a. strerror:返回指針指向由errnum所關(guān)聯(lián)的出錯(cuò)消息字符串的首地址。errnum的宏定義見errno.h。
char *strerror (int errnum)
{
           extern char *sys_errlist[];/*聲明了一個(gè)指針數(shù)組,著里面存放的是錯(cuò)誤信息*/
           extern int sys_nerr;
           if (errnum >= 0 && errnum < sys_nerr)
return sys_errlist[errnum];/*將錯(cuò)誤信息的語段返回*/
        return (char *) "Unknown error";
}
 
九.字符串反置操作
a. strrev: 將字符串全部翻轉(zhuǎn),返回指針指向翻轉(zhuǎn)后的字符串。
char * strrev(char *str)
{
             char *right = str;
             char *left = str;
             char ch;
             while (*right)  
    right++;
             right--;
             while (left < right)
             {
                   ch = *left;
                   *left++ = *right;
                      *right-- = ch;
             }
           return(str);
}
解析:
    ++,--用的很靈活,后置++ /--的優(yōu)先級(jí)高于*,前置++/--的優(yōu)先級(jí)和*相同,且均為右集合性。
強(qiáng)烈建議:
    盡量不要寫(*p++)之類的語句,一個(gè)語句就執(zhí)行一小步功能挺好。我曾在《C陷阱與缺陷》里看到作者指出某些C語言編譯器把(*p++)解釋為:(*p)++,你還敢寫么,真想寫也要寫成*(p++)。如果程序?qū)τ诖a空間的要求不太緊的話,不如分開寫,因?yàn)榭梢裕?/div>
1. 增加程序可讀性

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    日本人妻精品中文字幕不卡乱码| 日韩三级黄色大片免费观看| 亚洲视频一区二区久久久| 亚洲一区二区久久观看| 欧美日韩亚洲国产综合网| 日韩aa一区二区三区| 久草视频在线视频在线观看| 91欧美亚洲视频在线| 亚洲av熟女国产一区二区三区站| 国产日韩欧美在线播放| 欧美一区二区不卡专区| 99久久精品视频一区二区| 免费黄片视频美女一区| 欧美成人精品国产成人综合| 亚洲欧美日韩网友自拍| 国产精品免费视频视频| 欧美二区视频在线观看| 国产精品涩涩成人一区二区三区| 高清亚洲精品中文字幕乱码| 日韩欧美第一页在线观看| 日韩欧美国产精品自拍| 中文字幕91在线观看| 国产又粗又猛又爽色噜噜| 91人妻久久精品一区二区三区| 久久热在线视频免费观看| 亚洲中文字幕免费人妻| 亚洲国产一区精品一区二区三区色| 免费观看成人免费视频| 国产精品成人免费精品自在线观看| 日韩欧美国产亚洲一区| 国产日产欧美精品大秀| 伊人欧美一区二区三区| 国产欧美一区二区色综合| 国产精品成人一区二区三区夜夜夜| 欧美午夜一级艳片免费看| 午夜精品久久久99热连载| 色哟哟在线免费一区二区三区| 国产亚洲欧美自拍中文自拍| 日本一二三区不卡免费| 欧美一区二区三区视频区| 婷婷色国产精品视频一区|