一道Leetcode題引發的思考:Segmentation fault是什麼?

在Leetcode上刷題時,刷到題目Valid Anagram,給定兩個字符串st,編寫一個函數來肯定t是不是s的一個anagram,谷歌翻譯對anagram的解釋是經過從新排列另外一個單詞的字母順序而組成的一個新單詞,好比cinema是iceman的anagram。本質就是判斷st是否有同樣的字母組成。算法

什麼是Segmentation fault

我看到這個題目的第一印象就是對字符串st中的字母排序,而後逐一比對,若是所有相同,就返回true,不然返回false數組

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

int charcmp(const void *a, const void *b) {
    return *(char *)a - *(char *)b;
}

bool isAnagram(char* s, char* t) {
    int i;
    int s_len;
    int t_len;

    s_len = strlen(s);
    t_len = strlen(t);
    if (s_len != t_len) {
        return false;
    }
    qsort(s, s_len, sizeof(s[0]), charcmp);
    qsort(t, t_len, sizeof(t[0]), charcmp);
    for (i = 0; i < s_len; i++) {
        if (s[i] != t[i]) {
            return false;
        }
    }
    return true;
}

程序編譯沒有任何問題,但運行出錯,提示Segmentation fault (core dumped)。咦!好像見過,這不社區來着。查了查維基,存儲器段錯誤會出如今當程序企圖訪問CPU沒法定址的存儲器區塊時。函數

產生Segmentation fault的情景

stackoverflow上的一個回答有更詳細的解釋,存儲器段錯誤是因爲訪問沒有權限的內存而致使的一種錯誤,它能夠防止破壞內存和引入難調試的內存bug。
在C語言中有不少情景都會致使存儲器段錯誤,如對一個空指針解引用(dereference),性能

int *p = NULL;
*p = 1;

另外一種常見緣由是對只讀內存區域執行寫操做,這也是上段程序產生bug的緣由。翻譯

char *str = "Foo";
*str = 'b';

還有一種就是懸空指針,它指向根本不存在的東西。指針

char *p = NULL;
{
    char c;
    p = &c;
}

p是一個懸空指針,由於變量c是代碼塊做用域,代碼執行後,就被銷燬了,不復存在。此時,若是對p進行解引用,就可能會出現存儲器段錯誤,此處的代碼在gcc下並無報錯,是由於程序簡單。調試

解決問題

st是字符串常量,是不可修改的,我想對它從新排序,勢必須要改變字符串常量。解決辦法就是複製一份,對複製後的字符數組排序。code

bool isAnagram(char* s, char* t) {
    int i;
    int s_len;
    int t_len;
    char *s_cpy;
    char *t_cpy;

    s_len = strlen(s);
    t_len = strlen(t);
    s_cpy = (char *)malloc(sizeof(char)*s_len);
    t_cpy = (char *)malloc(sizeof(char)*t_len);
    strcpy(s_cpy, s);
    strcpy(t_cpy, t);

    if (s_len != t_len) {
        return false;
    }
    qsort(s_cpy, s_len, sizeof(s_cpy[0]), charcmp);
    qsort(t_cpy, t_len, sizeof(t_cpy[0]), charcmp);
    for (i = 0; i < s_len; i++) {
        if (s_cpy[i] != t_cpy[i]) {
            return false;
        }
    }
    return true;
}

問題解決了,不過對於這個問題還有更好的算法,使用哈希表。排序

bool isAnagram(char* s, char* t) {
    char alphabet[26];
    char *ptr;
    int i;
    
    for (i = 0; i < 26; i++) {
        alphabet[i] = 0;
    }
    for (ptr = s; *ptr != '\0'; ptr++) {
        alphabet[*ptr-'a']++;
    }
    for (ptr = t; *ptr != '\0'; ptr++) {
        alphabet[*ptr-'a']--;
    }
    for (i = 0; i < 26; i++) {
        if (alphabet[i] != 0) {
            return false;
        }
    }
    return true;
}

排序是須要成本的,使用哈希表就能夠節省下這個資源,性能就快了很多,哈希是4ms,上一個排序則是24ms。ip


參考資料

相關文章
相關標籤/搜索