《C與指針》第九章練習


本章問題

1.C語言缺少顯示的字符串數據類型,這是一個優點還是一個缺點?

answer:

(這個問題存在爭論(盡管我有一個結論))目前這個方法的優點是字符數組的效率和訪問的靈活性,它的缺點是有可能引起錯誤,數組溢出,下標越界,不能改變任何用於保存字符串的數組的長度等。我的結論是從現代的面向對象的奇數引出的,字符串類毫無例外的包括了完整的錯誤檢查,用於字符串的動態內存分配和其他一些防護措施,這些措施都會造成效率上的損失,但是,如果程序無法運行,效率再高也沒有什么意義,況且,現在軟件項目的規模比設計C語言的時代要大得多。因此,在數年前,缺少顯示的字符串類型還能被看成是一個優點,但是,由於這個方法內在的危險性,所以使用現代的高級的完整的字符串類還是物有所值的,如果C程序員願意循規蹈矩的使用字符串也可以獲得這些優點。

 

2.strlen函數返回一個無符號量(size_t),為什么這里無符號值比有符號值更合適?但返回無符號值其實也有缺點,為什么?

answer:It is more appropriate because the length of a string simply annot be vegative,Also,using an unsigned value allows longer string lengths(which would be negative in a signed quantity)to be represented.It is less appropriate becuase arithmetic involving unsinged expressions can yield unexpected results,The "advantage" of being able to report the length of longer strings is only rarely of value:on machines with 16 bit integers,it is needed only for strings exceeding 2147483647 bytes in length(which is rare indeed).

(更合適是因為一個字符串的長度不可能是個負數,而且無符號數的值允許字符串的長度更長,不適合是因為牽涉到算術運算時無符號表達四可能產生無法預料的結果,能夠允許字符串的長度更長這個“優點”其實很少使用,因為在16位機器上的整型,可以有2147483647位的長度,但實際很少使用)

 

3.如果strcat和strcpy返回一個指向目標字符串末尾的指針,和事實上返回一個指向目標字符串起始位置的指針相比,有沒有什么優點?

answer:Yes,then subsequent concatenations could be done more efficiently because the work of finding the end of the string would not need to be repeated.

(是的,指向目標字符串末尾的指針更高效,因為找到字符串的末尾的指針不需要重復工作)

 

4.如果從數組x復制50個字節到數組y,最簡單的方法是什么?

answer:

(使用memory庫函數,重要的是不要使用任何的str--函數,因為它們會在遇見第一個NUL字節的時候停止,如果你自己寫一個循環要更復雜,而且在效率上幾乎不可能超過這個函數)

 

5.假定你有一個名叫buffer的數組,它的長度為BSIZE個字節,你用下面這條語句把一個字符串復制到這個數組:

strncpy( buffer, some_other_string,BSIZE-1);

它能不能保證buffer中的內容是個有效的字符串?

answer:Only if the last character in the array is already NUL,A string must be terminated with a NUL byte,and strncpy does not guarantee that this will occur,However,the statement does not let strncpy change the last position in the array,so if that contains a NUL byte(either through an assignment or by the default initialization of static variables),then the result will be a string.

(只有字符 數組的最后一個字符確實是NUL的時候,一個字符串必須以NUL字節結尾,strncpy不會檢查有效性,然而,語句不會讓strncpy改變數組的最后一個位置,所以如果 最后一個位置是NUL字節(通過一個賦值或默認靜態變量的初始化),它的結果將會是個字符串

 

6.用下面這種方法

if(isalpha(ch))

取代下面這種顯式的測試有什么優點?

answer:First,the former will work regardless of the character set in use,The latter will work with the ASCII character set but will fail with the EBCDIC character set,Second,the former will work properly whether or not the locale has been changed;the latter may not.

(首先,前者不管在什么字符集中都能工作,而后者只能工作運行在ASCII字符集中,而在EBCDIC編碼中不能使用,第二,不管字母的區域是否改變前者都能正常運行,而后者不能)

 

7.下面的代碼怎樣簡化?

for(p_str = message; *p_str != '\0'; p_str++){
        if(islower(*p_str))
                *p_str = toupper(*p_str);
}

answer:

register int ch;
...
for(pstring = message; (ch = *pstring) != '\0';)
        *pstring++ = toupper(ch);

 

8.下面的表達式有何不同?

memchr(buffer,0,SIZE) - buffer;
strlen(buffer);

answer:

(如果緩沖區包含了一個字符串,memchr將在內存中buffer的起始位置查找第一個包含0的字節並返回一個指向該字節的指針,將這個指針減去buffer獲得存儲在這個緩沖區中的字符串長度,strlen完成相同的任務,不過strlen的返回值是個無符號類型的值,而指針減法應該是個有符號的值。但是,如果緩沖區內的數據不是以NUL結尾,memchr函數將返回一個NULL指針,將這個值減去buffer將產生一個無意義的結果,另一方面,strlen函數將在數組的后面繼續查找,知道最終發現一個NUL字節。盡管可以使用strlen函數獲得相同的結果,但一般而言,使用字符串函數不可能查找到NUL字節,因為這個值用於終止字符串,如果它是你需要查找的字節,你應該使用內存操縱函數。)

 

本章練習

1.編寫一個程序,從標准輸入讀取一些字符,並統計下列各類字符所占的百分比。

控制字符

空白字符

數字

小寫字母

大寫字母

標點符號

不可打印的字符

請使用在ctype.h頭文件中定義的字符分類函數

answer:

#include <stdio.h>
#include <ctype.h>

int main()
{
    int ch;
    float cntrl = 0;
    float space = 0;
    float digit = 0;
    float lower = 0;
    float upper = 0;
    float punct = 0;
    float unprint = 0;
    float total = 0;
    while((ch = getchar()) != EOF){
        total++;
        if(iscntrl(ch))
            cntrl++;
        if(isspace(ch))
            space++;
        if(isdigit(ch))
            digit++;
        if(islower(ch))
            lower++;
        if(isupper(ch))
            upper++;
        if(ispunct(ch))
            punct++;
        if(!isprint(ch))
            unprint++;
    }
    printf("control character is %% %3.0f\n",100 * (cntrl/total));
    printf("space character is %% %3.0f\n",100 * (space/total));
    printf("digit character is %% %3.0f\n",100 * (digit/total));
    printf("lower character is %% %3.0f\n",100 * (lower/total));
    printf("upper character is %% %3.0f\n",100 * (upper/total));
    printf("punct character is %% %3.0f\n",100 * (punct/total));
    printf("not print character is %% %3.0f\n",100 * (unprint/total));
}

 

2.編寫一個名叫my_strlen的函數,它類似與strlen函數,但它能夠處理由於使用strn--函數而創建的未以NUL字節結尾的字符串。你需要向函數傳遞一個參數,它的值就是保存了需要進行長度測試的字符串的數組的長度。

answer:

#include <stdio.h>
#include <stddef.h>

size_t my_strlen(char *string, int n)
{
    register size_t count = 0;
    while(*string++ != '\0' && n-- > 0)
        count++;
    return count;
}

 

3.編寫一個名為my_strcpy的函數,它類似與strcpy函數,但它不會溢出目標數組。復制的結果必須是一個真正的字符串。

answer:

char *my_strcpy(char *dst, char *src, int size){
    strncpy(dst, src, size);
    *(dst + size - 1) = '\0';
    return dst;
}

 

4.編寫一個名叫my_strcat的函數,它類似於strcat函數,但它不會溢出目標數組,它的結果必須是一個真正的字符串。

answer:

char *my_strcat(char *dst, char *src, int size){
    size_t length = size - 1 - my_strlen(dst,size);
    if(length > 0){
        strncat(dst,src,length);
        *(dst + size - 1) = '\0';
    }
    return dst;
}

 

5.編寫函數

void my_strncat(char *dest, char *src, int dest_len);

它用於把src中的字符串連接到dest中原有的字符串末尾,但它保證不會溢出長度為dest_len的dest數組,和strncat函數不同,這個函數也考慮原先存在於dest數組的字符串長度,因此能夠保證不會超越數組邊界。

void my_strncat(char *dest, char src, int dest_len)
{
    register int len;
    len = strlen(dst);
    dest_len -= len + 1;
    if(dest_len > 0)
        strncat(dest, src, dest_len);
}

 

6.編寫一個名叫my_strcpy_end的函數取代strcpy,它返回一個指向目標字符串末尾的指針(也就是說,指向NUL字節的指針),而不是返回一個指向目標字符串起始位置的指針。

answer:

//兩種方法
char *my_strcpy_end(char dst,char src)
{
    strcpy(dst,src);
    int length = strlen(dst);    
    dst += length;
    return dst;
}

char *my_strcpy_end(char dst,char src)
{
    while(*src != '\0')
        *dst++ = *src++;
    return dst - 1;
}

 

7.編寫一個名叫my_strrchr的函數,它的原型如下:

char *my_strrchr(char const *str,int ch);

這個函數類似與strchr函數,只是它返回的是一個指向ch字符在str字符串中最后一次出現的位置的指針。

answer:

char *my_strrchr(char const *str,int ch)
{
    char *p = strchr(str,ch);
    while(*p != NULL){
        str = p++;
        p = strchr(str,ch);
    }
    return str; 
}

 

8.編寫一個名叫my_strnchr的函數,它的原型如下:

char *my_strnchr(char comst *str, int ch, int which);

這個函數類似於strchr,但它的第三個參數指定ch字符在str字符串中第幾次出現,例如,第三個參數為1,這個函數的功能就和strchr一樣,如果參數為2,這個函數就返回一個指向ch字符在str字符串中第二次出現的位置的指針。

answer:

char *my_strnchr(char const *str, int ch, int which)
{
    char *p = strchr(str,ch);
    while(*p != NULL && --which > 0){
        str = p++;
        p = strchr(str,ch);
    }
    return p;
}

 

9.編寫一個函數,它的原型如下:

int count_chars(char const *str,char const *chars);

函數應該在第一個參數中進行查找,並返回匹配第二個參數所包含的字符的數量。

int count_chars(char const *str, char const *chars)
{
    int count = 0;
    while((str = strnbrk(str,chars)) != NULL){
        count++;
        str++;
    }
    return count;
}

 

10.編寫函數

int palindrome(char *string);

如果參數字符串是個回文,函數就返回真,否則返回假。回文就是指一個字符串從左向右和從右向左讀是一樣的。函數應該忽略所有的非字母字符,而且在進行字符比較時不用區分大小寫。

answer:

#include <stdio.h>
#include <ctype.h>

int palindrome(char *string)
{
    char *p = string;
    char *q = string;
    while(*q != '\0')
        q++;
    q--;
    while(p <= q){
        while(!isalpha(*p))
            p++;
        while(!isalpha(*q))
            q--;
        *p = tolower(*p);
        *q = tolower(*q);
        if(p > q)
            break;
        if(*p != *q)
            return 0;
        p++;
        q--;
    }
    return 1;
}

 

11.編寫一個程序,對標准輸入進行掃描,並對單詞“the”出現的次數進行計數,進行比較時應該區分大小寫,所以“The”和“THE”並不計算在內。你可以認為各單詞由一個或多個空格字符分隔,而且輸入行在長度上不超過100個字符,計數結果應該寫到標准輸出上。

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int count_the(char *string)
{
    int count = 0;
    char whitespace[] = " \t\f\r\v\n";
    char *p;
    for(p = strtok(string,whitespace);
        p != NULL;
        p = strtok(NULL,whitespace)){
        if(strcmp(p,"the") == 0)
            count++;
    }
    return count;
}

int main()
{
    char string[100];
    gets(string);
    printf("\"the\" is appeared %d times",count_the(string));
    return 0;
}

 

12.有一種技巧可以對數據進行加密,並使用一個單詞作為它的密鑰。下面是它的工作原理:首先,選擇一個單詞作為密鑰,如TRAILBLAZERS。如果單詞中包含有重復的字母,只保留第一個,其余幾個丟棄。現在,修改過那個單詞列於字母表的下面,如下所示:

最后,底下那行字母表中剩余的字母填充完整:

在對信息進行加密時,信息中的每個字母被固定於頂上那行,並用下面那行的對應字母一一取代原文的字母。因此,使用這個密鑰,ATTACK AT DAWN(黎明時攻擊)就會被加密為TPPTAD TP ITVH。這個題材共有三個程序(包括下面兩個練習),在第一個程序中,你需要編寫函數:

int prepare_key(char *key);

它接受一個字符串參數,它的內容就是需要使用的密鑰單詞,函數根據上面描述的方法把它轉換成一個包含編好碼的字符數組。假定key參數是個字符數組,其長度至少可以容納27個字符。函數必須把密鑰中的所有字符要么轉換成大寫,要么轉換成小寫(隨你選擇),並從單詞中去除重復的字母,然后再用字母表中剩余的字母按照你原先所選擇的大小寫形式填充到key數組中去,如果處理成功,函數將返回一個真值,如果key參數為空或包含任何非字母字符,函數將返回一個假值。

answer:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

char character[27] = "abcdefghijklmnopqrstuvwxyz";

//delete the char *ch for char *string
void del_ch(char *string, char *ch)
{
    char *p = ch + 1;
    while(*p != '\0'){
        *ch++ = *p++;
    }
    *ch = *p;
}

int init_key(char *key)
{
    while(*key != '\0'){
        if(!isalpha(*key))
            return 0;
        *key = tolower(*key);
        key++;
    }
    return 1;
}

void delete_rech(char *key)
{
    char ch;
    char *p,*q;
    while((ch = *key++) != '\0'){
        while((p = strchr(key, ch)) != NULL){
            del_ch(key,p);
        }
    }
}

void fill_key(char *key)
{
char string[27];
strncpy(string,character,27);
char *p;
while((p = strpbrk(string,key)) != NULL){
    del_ch(string,p);
}
strcat(key,string);
}

int prepare_key(char *key)
{
    if(*key == '\0')
        return 0;
        
    //is alpha? and to lower
    if(init_key(key) == 0)
        return 0;

    //delete the character which is repeat
    delete_rech(key);

    //fill the key
    fill_key(key);
    
    return 1;
}

int main()
{
    char key[27] = "TRAILBLAZERS";
    printf("original key:%s\n",key);
    if(prepare_key(key) == 0)
        printf("the key is error!\n");
    else
        printf("prepare key:%s\n",key);
    return 0;
}

運行結果:

 

13.編寫函數

void encrypt(char *data, char const *key);

它使用前題prepare_key函數所產生的密鑰對data中的字符進行加密。data中的非字母字符不作修改,但字母字符則用密鑰所提供的編過碼的字符一一取代源字符。字符的大小寫狀態應該保留。

answer:

void encrypt(char *data, char const *key)
{
    char *p = data;
    while(*p != '\0'){
        if(islower(*p)){
            *p = key[*p - 'a'];
        }else if(isupper(*p)){
            char ch = tolower(*p);
            ch = key[ch - 'a'];
            *p = toupper(ch);
        }
        p++;
    }
}

運行結果:

 

14.這個問題的最后部分就是編寫函數

void decrypt(char *data, char const *key);

它接受一個加過密的字符串為參數,它的任務是重現原來的信息,除了它是用於解密之外,它的工作原理應該與encrypt相同。

answer:

void decrypt(char *data, char const *key)
{
    char *p = data;
    while(*p != '\0'){
        if(islower(*p)){
            char *k = strchr(key,*p);
            int num = k - key;
            *p = character[num];
        }else if(isupper(*p)){
            char ch = tolower(*p);
            char *k = strchr(key,ch);
            int num = k - key;
            ch = character[num];
            *p = toupper(ch);
        }
        p++;
    }
}

運行結果:

 

15.標准I/O庫並沒有提供一種機制,打印大整數時逗號進行分隔。在這個練習中,你需要編寫一個程序,為美元數額的打印提供這個功能。函數把一個數字字符串(代表以美分為單位的金額)轉換為美元形式,如下面的例子所示:

下面是函數的原型:

void dollars(char *dest, char const *src);

src將指向需要被格式化的字符(你可以假定它們都是數字)。函數應該像上面例子所示的那樣對字符進行格式化,並把結果字符串保存到dest中。你應該保證你所創建的字符串以一個NUL字節結尾。src的值不應被修改。你應該使用指針而不是下標。

提示:首先找到第2個參數字符串的長度,這個值有助於判斷逗號應插入到什么位置,同時,小數點和最后兩位數字應該是唯一需要你進行處理的特殊情況。

answer:

 

void dollars(char *dest, char const *src)
{
    int len;
    if(dest == NULL || src == NULL)
        return ;

    *dest++ = '$';
    len = strlen(src);
    if(len >= 3){
        int i;
        for(i = len - 3; i > 0){
            *dest++ = *src++;
            if(--i > 0 && i % 3 == 0)
                *dest++ = ',';
        }
    }
    else
        *dest++ = '0';

    *dest++ = '.';
    *dest++ = len < 2 ? '0' : *src++;
    *dest++ = len < 1 ? '0' : *src;
    *dest = 0;
}

 

16.這個程序與前一個練習相似,但它更為通用。它按照一個指定的格式字符串對一個數字字符串進行格式化,類似許多BASIC編碼器所提供的“print using”語句。函數的原型如下:

int format(char *format_string, char const *digit_string);

digit_string中的數字根據一開始在format_string中找到的字符從右到左逐個復制到format_string中。注意被修改后的format_string就是這個處理過程的結果,當你完成時,確定format_string依然是以NUL字節結尾的。根據格式化過程中是否出現錯誤,函數返回真或假。

格式字符串可以包括下列字符:

#  在兩個字符串中都是從右向左進行操作,格式字符串中的每個#字符都被數字字符串中的下一個數字取代,如果數字字符串用完,格式字符串中所有剩余的#字符由空白代替(但存在例外,請參見下面對小數點的討論)。

, 如果逗號左邊至少有一位數字,那么它就不作修改。否則它由空白取代。

.   小數點始終作為小數點存在。如果小數點左邊沒有一位數字,那么小數點左邊的那個位置以及右邊直到有效數字為止的所有位置都由0填充。

下面的例子說明了對這個函數的一些調用的結果。符號Ø用於表示空白。

為了簡化這個項目,你可以假定格式字符串所提供的格式總是正確的。最左邊至少有一個#符號,小數點和逗號的右邊也至少有一個#符號。而且逗號絕不會出現在小數點的右邊,你需要進行檢查的錯誤只有:

a  數字字符串中的數字多余格式字符串中的#符號

b  數字字符串為空

發生這兩種錯誤時,函數返回假,否則返回真。如果數字字符串為空,格式字符串在返回時應未作修改。如果你使用指針而不是下標來解決問題,你將會學到更多的東西。

 提示:開始時讓兩個指針分別指向格式字符串和數字字符串末尾,然后從右向左進行處理,對於作為參數傳遞給函數的指針,你必須保留它的值,這樣你就可以判斷是否到達了這些字符串的左端。

int format(char *format_string, char const *digit_string)
{
    char *fs, *ds;
    fs = format_string + strlen(format_string) - 1;
    ds = digit_string + strlen(digit_string) - 1;
    if(digit_string == NULL)
        return 0;

    while(fs >= format_string){
        if(ds >= digit_string){
            switch(*fs){
            case '#':
                *fs-- = *ds--;
                break;
            case '.':
                fs--;
                break;
            case ',':
                fs--;
                break;
            }
        }else{
            char *p = strchr(format_string,'.');
            if(p == NULL){
                *fs-- = ' ';
            }else{
                if(fs < p - 1)
                    *fs-- = ' ';
                else if(fs == p)
                    *fs-- = '.';
                else
                    *fs-- = '0';
            }
        }
    }
    if(ds >= digit_string){
        printf("digit_string more than format_string\n");
        return 0;
    }
    return 1;
}

 答案給出的解法和上面我自己的解法有所不同,但是代碼思路差別不大,不過不管是以上代碼還是標准答案的代碼都用到了兩個比較:

fs >= format_string
ds >= digit_string

關於這個比較,存在一些問題,下面是標准答案給出的解釋:

大意是說,這個測試是非法的,因為當它不滿足while語句的時候,不管是ds(答案用的是digitp)指針還是fs(答案用的是patternp)指針都超出了數組最左邊的范圍,另一方面,它有可能(通常不可能)調用下面這個函數:

if(format(p_array+1,d_array+1))...

這種情況沒有違反什么,有趣的部分在於指針的算術比較運算需要在合法的情況下工作,而上面顯示的函數導致它在非法的情況下也能工作,事實上,只有一種情況函數的運行會失敗,就是如果其中一個數組是從0這個內存位置開始的,從數組的開始實際覆蓋的內存之前計算一個指針和從內存空間的末尾產生的指針,這些都會使比較失敗。在大多數機器上這種情況將不會發生,因為0代表NULL指針,所以編譯器不會把任何數據放到這里,然而,這仍然存在一個危險,標准允許NULL指針可以轉換成任何類型的指針,即使在源代碼中總是用0來代表NULL,在這種情況下,上面描述的情況可能會發生,會讓調試變得困難。這個函數應該移除這個依賴性,這里給出的錯誤版本僅僅是引起討論。

 

17.這個程序與前兩個練習類似,但更加一般化了,它允許調用程序把逗號放在大數的內部,去除多余的前導0以及提供一個浮動的美元符號等。

這個函數的操作類似於IBM370機器上的Edit和Mark指令。它的原型如下:

char *edit(char *pattern, char const *digits);

它的基本思路很簡單,模式(pattern)就是一個圖樣,處理結果看上去應該向它的樣子。數字字符串中的字符根據這個圖樣所提供的方式從左向右復制到模式字符串。數字字符串的第一位有效數字很重要,結果字符串中所有在第一位有效數字之前的字符都由一個“填充”字符代替,函數將返回一個指針,它所指向的位置正是第一位有效數字存儲在結果字符串中的位置(調用程序可以根據這個返回指針,把一個浮動美元符號放在這個值左邊的毗鄰位置)。這個函數的輸出結果就像支票上打印的那樣--這個值左邊所有的空白符由星號或其他字符填充。

在描述這個函數的詳細處理過程之前,看一些這個操作的例子是很有幫助的,為了清晰起見,符號Ø用於表示空格。結果字符串中帶下划線的那個數字就是返回值指針所指想的字符(也就是第一個有效數字),如果結果字符串中不存在帶下划線的字符,說明函數返回值是個NULL指針。

 現在讓我們來討論這個函數的細節。函數的第一個參數就是模式,模式字符串的第一個字符就是“填充字符”。函數使數字字符串修改模式字符串中剩余的字符來產生結果字符串。在處理過程中,模式字符串將被修改,輸出字符串不可能比原先的模式字符串更長,所以不存在溢出第一個參數的危險(因此不需要對此進行檢查)。

模式是從左向右逐個字符進行處理的。每個位於填充字符串后面的字符的處理結果將是三中選一

a  保持原樣不被修改

b  被一個數字字符串中的字符代替

c  被填充字符取代

數字字符串也是從左向右進行處理的,但它本身在處理過程中絕不會被修改,雖然它被稱為“數字字符串”,但它也可以包含任何其他字符,如上面的例子之一所示,但是,數字字符串中的空格應該和0一樣對待(它們的處理的結果相同)。函數必須保持一個“有效標識”,用於標識是否有任何有效數字從數字字符串復制到模式字符串。數字字符串中的前導空格和前導0並非有效字符,其余的字符都是有效字符。

如果模式字符串或數字字符串有一個是NULL,那就是個錯誤,在這種情況下,函數應該立即返回NULL。

下面這個表列出了所有需要的處理過程。列標題“signif”就是有效標識,“模式”和“數字”分別代表模式字符串和數字字符串的下一個字符。表的左邊列出了所有可能出現的不同情況,表的右邊描述了每種情況需要處理的過程,例如如果下一個模式字符是“#”,有效標識就設為假,數字字符串的下一個字符是‘0’,所以用一個填充字符代替模式字符串中的#字符,對有效標識不作修改。

answer:

char *edit(char *pattern, char const *digits)
{
    char fillch = *pattern++;
    int signif = 0;
    char *sign;
    char *ps = pattern;
    char *ds = digits;
    if(pattern == NULL || digits == NULL)
        return NULL;
    
    while(*ps != '\0'){
        switch(*ps){
        case '\0':
            break;
        case '#':
            if(*ds == '\0')
                *ps = '\0';
            else if(signif == 0){
                if(*ds == '0' || *ds == ' '){
                    ds++;
                    *ps = fillch;
                }
                else{
                    *ps = *ds++;
                    signif = 1;
                    sign = ps;
                }
            }else
                *ps = *ds++;
            break;
        case '!':
            if(*ds == '\0')
                *ps = '\0';
            else if(signif == 0){
                *ps = *ds++;
                signif = 1;
                sign = ps;
            }else
                *ps = *ds++;
            break;
        default:
            if(signif == 0)
                *ps = fillch;
            break;
        }
        ps++;
    }
    
    return sign;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM