2016-09-02 29 views
-1

我讀了關於指向tutorialsPoint上指針的指針。如何使用指向切片字符串的指針

我有一點自我測試。我想按空格分割一個字符串,以便每個單詞(包括標點符號)都被視爲一個標記,並且這些標記將逐行返回。

下面是代碼:

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

char** split(const char* s) 
{ 
    int i = 0, j = 0; 
    char** word = malloc(strlen(s)+1); 
    * word = malloc(strlen(s)+1); 

    while (*s != '\0') { 
     if (*s == ' ') { 
      i++; 
     } else { 
      word[i][j] = *s; 
      j++; 
     } 
     i++; 
     s++; 
    } 

    return word; 
    //free(word); //with or without this i get the same outcome. 
} 

int main(void) 
{ 
    char** words = split("He said 'hello' to me!"); 
    int i = 0; 
    while (words[i] != NULL) { 
     puts(words[i]); 
     free(words[i]); 
     i += 1; 
    } 
    free(words); 
} 

它編譯,但是當我在終端上運行,我得到段錯誤。我在if語句中添加了一個printf,它會打印每一個字母。

我也用valgrind,但我不明白它在說什麼。

預期輸出:

He 
said 
'hello' 
to 
me! 
+0

由malloc保護的區域不正確。 – BLUEPIXY

+0

我是否必須分配與令牌完全相同的內存大小? – chrisgjh

+0

@chrisgjh你可以,但你沒有。我記得當你做同樣的事情時,我會先學習更多地使用一點點記憶,當我明白這是如何工作的時候,我就能夠做到與記憶完全相同的記憶尺寸! ;) – gsamaras

回答

0

首先,我會在你嘗試評論:

這裏:

return word; 
free(word); 

免費將不會被執行!你看到return聲明後的任何內容,都不執行!

此外,使用malloc()動態分配的空間是錯誤的,請檢查我們如何在我的2D dynamic array中執行此操作,或者/並閱讀下面的示例。

你看,而不必分配充足的,你在訪問了綁定的內存,這將導致未定義行爲的,但你是幸運地得到一個分段錯誤,這提醒您! :)

我不會調試你的代碼,因爲這是一個很好的機會練習,但如果你想要更多。

下面是我該怎麼做,這是一個更簡單的方法,但如果你明白了,那麼你將能夠自己嘗試工作! :)

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

// We return the pointer 
char **get(int N, int M) // Allocate the array */ 
{ 
    // TODO: Check if allocation succeeded. (check for NULL pointer) 
    int i; 
    char** table; 
    table = malloc(N*sizeof(char *)); 
    for(i = 0 ; i < N ; i++) 
     table[i] = malloc(M*sizeof(char)); 
    return table; 
} 

void free2Darray(char** p, int N) { 
    int i; 
    for(i = 0 ; i < N ; i++) 
     free(p[i]); 
    free(p); 
} 

void zeroFill(char** p, int N, int M) { 
    int i, j; 
    for(i = 0 ; i < N ; i++) 
     for(j = 0 ; j < M ; j++) 
      p[i][j] = 0; 
} 

void print(char** p, int N, int M) { 
    int i; 
    for(i = 0 ; i < N ; i++) 
     if(strlen(p[i]) != 0) 
      printf("array[%d] = %s\n", i, p[i]); 
} 

void split(const char* s, char** words) { 
    int i = 0, word_idx = 0, char_idx = 0; 
    while(s[i] != '\0') { 
     if(s[i] != ' ') { 
      words[word_idx][char_idx++] = s[i]; 
     } else { 
      word_idx++; 
      char_idx = 0; 
     } 
     ++i; 
    } 
} 

int main(void) 
{ 
    char** words = get(10, 15); // 10 words, 14 chars max (+1 for n$ 
    zeroFill(words, 10, 15); 

    split("He said 'hello' to me!", words); 
    print(words, 10, 15); 

    free2Darray(words, 10); 
    return 0; 

} 

輸出:

C02QT2UBFVH6-lm:~ gsamaras$ nano main.c 
C02QT2UBFVH6-lm:~ gsamaras$ gcc -Wall main.c 
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out 
array[0] = He 
array[1] = said 
array[2] = 'hello' 
array[3] = to 
array[4] = me! 

的解釋:

  1. 我認爲一個句子最多有10個字,每個字都會 最多爲14個字符( +1爲NULL結束符)。
  2. 動態分配10行15列的二維數組。
  3. 將其所有元素初始化爲零。我這樣做只是爲了確保 單詞是NULL終止。我當然可以跳過這一步,然後 手動在 split()的確切位置插入NULL終止符。
  4. 將令牌字符串拆分爲令牌。
  5. 打印文字。
  6. 釋放2D陣列的動態分配內存。

現在,讓我解釋一點的split()功能:

其實你的嘗試是相當不錯的,你應該知道我現在做的已經什麼:

在字符串
  1. 我環路,直到我遇到NULL終止符。
  2. 只要我不符合空格,我在當前字中插入當前的 字符。 word_idx幫我記得現在我填寫的是哪個詞 ,以及那個詞的哪個字符與 char_idx
  3. 當我遇到空格(else的情況下),我必須前進到 下一個單詞(無需NULL-終止字符串填充 之前,因爲我有零初始化我的二維數組),通過遞增 word_idx由1.我還設置char_idx爲0,這樣我可以開始 從位置填滿新字0
1

split修復這樣的。

char **split(const char *s){ 
    int i = 0, j; 
    int len = strlen(s); 
    len += (len & 1);//+1 if len is odd 
    char **word = malloc((len/2 + 1) * sizeof(*word));//+1 for NULL 

    while (*s) { 
     while(*s == ' ') 
      ++s;//skip spaces 
     if(!*s) 
      break; 
     for(len = 0; s[len] && s[len] != ' '; ++len) 
      ; 
     word[i] = malloc(len + 1); 
     for(j = 0; j < len; ++j) 
      word[i][j] = *s++; 
     word[i++][j] = '\0'; 
    } 
    word[i] = NULL; 
    return word; 
} 
+0

'malloc'的檢查返回值被省略。 – BLUEPIXY