C之典型字符串(二十八)

        我們在上節博客中介紹了 C 語言中字符串相關的概念,那麼我們今天就來看看在字符串這塊的典型問題。

        A、我們先來看看下面的示例代碼會輸出什麼,代碼如下

#include <stdio.h>

int main()
{
    char buf[15] = {0};
    char src[] = "hello %s";
    
    snprintf(buf, sizeof(buf), src);
    
    printf("buf = %s\n", buf);
    
    return 0;
}

        我們先來說說 snprintf 函數,它本身是可變參數的函數,原型是這樣的:int snprintf(char* buf, int buf_size, const char* fomart, ...)。當函數只有3個參數時,如果第三個參數沒有包含格式化信息,函數調用沒有問題;相反,如果第三個參數包含了格式化信息,但缺少後續對應參數,則程序行爲不穩定。上面的程序中第8行調用了 snprintf 函數,但是在第6行定義的 src 字符數組中包含了 %s,則它的行爲是不確定的。我們來看看編譯結果圖片.png

        我們看到編譯其實已經提示了,打印的結果果然是不確定的。那麼我們在 snprintf 函數中再加上第四個參數字符串“world”試試(或者直接把第6行後面的 %s 變成 world 也是同樣的效果)。編譯結果如下

圖片.png

        那麼我們看到編譯沒有警告,程序也完美運行。

        B、我們再來看看下面這份示例代碼

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

int main()
{
    #define STR "Hello, \0World\0"
    
    char* src = STR;
    char buf[255] = {0};
    
    snprintf(buf, sizeof(buf), src);
    
    printf("strlen(STR) = %d\n", strlen(STR));
    printf("sizeof(STR) = %d\n", sizeof(STR));
    
    printf("strlen(src) = %d\n", strlen(src));
    printf("sizeof(src) = %d\n", sizeof(src));
    
    printf("strlen(buf) = %d\n", strlen(buf));
    printf("sizeof(buf) = %d\n", sizeof(buf));
    
    printf("src = %s\n", src);
    printf("buf = %s\n", buf);
    
    return 0;
}

        我們先來分析下這個程序,第6行定義了一個宏,但是它裏面有兩個 \0,實際上是3個,因爲編譯器還會爲字符串自動去分配個 \0。在程序的第11行進行 src 到 buf 的內容複製。我們在上屆說過字符數組是以 \0 結尾的,因此第13行打印的長度爲7。但第14行打印的是它整個宏定義的長度,所以爲15。第16行打印的也便是 7 了,第17行打印的指針的長度,便是4。第19行打印的 buf 中內容的長度同樣也是 7,第20行打印的 數組 buf 的長度便是 255。第22和23行分別打印 src 和 buf 中的內容,便是 hello 了。我們來看看編譯結果圖片.png

        結果和我們分析的一致,字符串字面量的本質爲數組。

        C、再來看第三個示例程序,代碼如下

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

int main()
{
    #define s1 "hello world"
    #define s2 "hello world"
    
    if( s1 == s2 )
    {
        printf("Equal\n");
    }
    else
    {
        printf("Non Equal\n");
    }
    
    if( strcmp(s1, s2) == 0)
    {
        printf("Equal\n");
    }
    else
    {
        printf("Non Equal\n");
    }
    
    return 0;
}

        我們先來分析下,我們在第6和7行分別定義了兩個宏字符串(但是它們的內容是相同的)。接下來我們直接將 s1 和 s2 進行判斷是否相等。那麼在這塊我們判斷的應當是他兩的地址,它們在這塊就是數組,兩個數組怎麼可能進行相等比較呢。如果是判斷地址,第一個 if 語句應當打印出不相等的。下面的 if 語句是用 strcmp 函數進行判斷的餓,那麼這個當然是相等的啦,因爲這個函數判斷的是他兩的內容。所以經我們分析,第一個 if 語句打印出 Non Equal,第二個 if 語句打印出 Equal。我們來看看編譯器就是是怎麼處理的

圖片.png

        我們看到第一個和我們分析的不一樣,那麼我們再來看看 BCC 編譯器

圖片.png

        那麼 BCC 編譯器的結果和我們分析的是一致的。在 gcc 編譯器中它做了優化,當我們定義 s1 之後,進行 s2 的定義時。編譯器發現他倆內容是一樣的,便將 s2 也指向了 s1 的地址,因爲它覺得你是在浪費內存。我們在程序中加上打印 s1 和 s2 的地址的語句,gcc 打印結果如下

圖片.png

        gcc 編譯器果然是將他倆放在一個地址上了。但是我們看看 BCC 呢

圖片.png

        我們看到 BCC 是這樣的,所以我們在以後不能寫出依賴於某種編譯器的代碼,這樣的話,代碼的可移植性就降低了。所以我們在進行字符串之間的相等比較時需要用 strcmp 完成,不可直接用 == 進行字符串直接進行比較。完全相同的媳婦吃字面量的 == 比較結果爲 false。一些現代編譯器能夠將相同的字符串字面量映射到同一個無名字符數組,因此 == 比較結果爲 true。

        D、最後我們再來看個關於字符串循環右移的問題,這也是一道筆試面試題。代碼如下

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

void right_shift_r(const char* src, char* result, unsigned int n)
{
    const unsigned int len = strlen(src);
    int i = 0;
    
    for(i=0; i<len; i++)
    {
        result[(i+n) % len] = src[i];
    }
    
    result[len] = '\0';
}

int main()
{
    char result[255] = {0};
    
    right_shift_r("abcde", result, 2);
    
    printf("%s\n", result);
    
    right_shift_r("abcde", result, 5);
    
    printf("%s\n", result);
    
    right_shift_r("abcde", result, 8);
    
    printf("%s\n", result);
    
    return 0;
}

        我們可以利用求餘的的方式進行字符串的賦值。那麼我們用一個 for 循環就完成右移,它的時間複雜度爲 O(n),這個效率無疑是最高的。


        歡迎大家一起來學習 C 語言,可以加我QQ:243343083

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章