1. 程式人生 > >C之典型字符串(二十八)

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

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,則它的行為是不確定的。我們來看看編譯結果技術分享圖片

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

技術分享圖片

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

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 了。我們來看看編譯結果技術分享圖片

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

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。我們來看看編譯器就是是怎麽處理的

技術分享圖片

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

技術分享圖片

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

技術分享圖片

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

技術分享圖片

我們看到 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

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