我試圖制作一個文本決議器,它根據空格字符分隔字串中的單詞。但是,出了點問題。
#include <stdio.h>
#include <string.h>
int main() {
//the string should end with a space to count the all words
char name[30] = "hello world from jordan ";
int start = 0;
int end = strlen(name);
int end_word = start;
char full[20][20];
memset(full, 0, 400);
int number_of_words = 0;
for (int w = 0; w < end; w ) {
if (name[w] == ' ') {
number_of_words ;
}
}
int counter = 0;
while (counter < number_of_words) {
for (int i = start; i < end; i ) {
if (name[i] == ' ') {
start = i;
break;
}
}
for (int j = end_word; j < start; j ) {
full[counter][j] = name[j];
}
end_word = start;
start ;
counter ;
}
for (int x = 0; x < 20; x ) {
for (int y = 0; y < 20; y ) {
printf("%c", full[x][y]);
}
printf("%d", x);
}
return 0;
}
這是我運行代碼時發生的奇怪事情:
hello0 world1 from2 jor3dan45678910111213141516171819
前三個詞正在以正確的方式初始化,但第四個不是,我不知道為什么會這樣。
我想要對問題的解釋,如果可能的話,我想要一種更有效的方式來撰寫此代碼,而無需使用指標。
注意:我是初學者,這就是為什么我要求沒有指標的解決方案。
uj5u.com熱心網友回復:
首先,試圖避免 C 中的指標將(非常)困難。就其本質而言,當您想對它們做任何有用的事情時,陣列就會變成指標。陣列訂閱是指標演算法的語法糖(foo[2]與 相同*(foo 2))。將陣列傳遞給函式將導致它衰減為指向第一個元素的指標。
無論您是否意識到,您都會在代碼中多次使用指標。
至于代碼...
快速說明:size_t, not int,是處理記憶體大小/索引時使用的合適型別。我將在代碼的“更正”版本中使用它,您應該嘗試在一般情況下使用它,繼續前進。
輸出有點混亂,因為所有內容都列印在一行上。讓我們清理一下,并添加一些除錯資訊,例如您存盤的每個字串的長度。
for (size_t x = 0; x < 20; x ) {
printf("%zu [length: %zu]: ", x, strlen(full[x]));
for (size_t y = 0; y < 20; y )
printf("%c", full[x][y]);
putchar('\n');
}
現在我們得到了跨越幾行的輸出(為簡潔起見,一些重復折疊),如下:
0 [length: 5]: hello
1 [length: 0]: world
2 [length: 0]: from
3 [length: 0]: jor
4 [length: 3]: dan
5 [length: 0]:
...
19 [length: 0]:
從這里我們可以看到一些值得注意的事情。
- 當我們只期待四個時,我們有一個額外的第五個“字串”。
- 我們的第一個和第五個“字串”具有明顯正確的長度,而
- 我們的第二個到第四個“字串”的明顯長度為
0,并且似乎包含空格。
零長度意味著我們的一些陣列以空終止位元組 ( '\0')開頭,并且我們只看到輸出是因為我們手動遍歷了每個陣列的整體。
請注意,當要列印空字符時,大多數終端將“什么都不做”,這意味著我們似乎直接跳到我們的“字串”。我們可以通過總是列印一些東西來更好地可視化正在發生的事情:
printf("%c", full[x][y] ? full[x][y] : '*');
在這種情況下,我們會在遇到空字符時列印一個星號,從而為我們提供輸出:
0 [length: 5]: hello***************
1 [length: 0]: ***** world*********
2 [length: 0]: *********** from****
3 [length: 0]: **************** jor
4 [length: 3]: dan*****************
5 [length: 0]: ********************
...
19 [length: 0]: ********************
這非常清楚地顯示了我們的角色在記憶體中的位置。
主要問題是在這個回圈中
for (int j = end_word; j < start; j ) {
full[counter][j] = name[j];
}
j初始化為相對于 的開頭的位置name,但用于索引 的記憶體偏移量full。排除我們的第一個子字串 when end_wordis 0,這讓我們離每個子陣列的第零個索引越來越遠,最終跨越了陣列之間的邊界。
這恰好起作用,因為 C 中的 2D 陣列在記憶體中是連續布局的。
為了解決這個問題,我們必須使用一個單獨的索引來復制我們的字符,每個子陣列從零開始。
for (size_t j = end_word, k = 0; j < start; j , k ) {
full[counter][k] = name[j];
}
現在,當我們列印出我們的陣列時,我們可以將自己限制在我們已知的number_of_words( for (size_t x = 0; x < number_of_words; x )) 中,給我們輸出:
0 [length: 5]: hello***************
1 [length: 6]: world**************
2 [length: 5]: from***************
3 [length: 7]: jordan*************
這看起來大致正確,但在“單詞”中包含了前面的空格。我們可以通過設定end_word為下一個字符來跳過這些空格:
start ;
end_word = start;
counter ;
現在我們的輸出看起來正確分割:
0 [length: 5]: hello***************
1 [length: 5]: world***************
2 [length: 4]: from****************
3 [length: 6]: jordan**************
請注意,這些是(現在正確格式化的)以空字符結尾的字串,并且可以使用說明%s符列印,如下所示:
for (size_t x = 0; x < number_of_words; x )
printf("%zu [length: %zu]: %s\n", x, strlen(full[x]), full[x]);
總的來說,這有點脆弱,因為它需要尾隨定界空間才能作業,并且每次重復定界空格時都會創建一個空字串(或者如果源字串以空格開頭)。
順便說一句,這個類似的示例應該展示一種用于標記字串的直接方法,同時跳過所有分隔符,并包含一些重要的注釋。
#include <stdio.h>
#include <string.h>
int main(void) {
char name[30] = "hello world from jordan";
char copies[20][30] = { 0 };
size_t length_of_copies = 0;
size_t hold_position = 0;
size_t substring_span = 0;
size_t i = 0;
do {
/* our substring delimiters */
if (name[i] == ' ' || name[i] == '\0') {
/* only copy non-zero spans of non-delimiters */
if (substring_span) {
/* `strncpy` will not insert a null terminating character
* into the destination if it is not found within the span
* of characters of the source string...
*/
strncpy(
copies[length_of_copies],
name hold_position,
substring_span
);
/* ...so we must manually insert a null terminating character
* (or otherwise rely on our memory being initialized to all-zeroes)
* */
copies[length_of_copies ][substring_span] = '\0';
substring_span = 0;
}
/* let's assume our next position will be the start of a substring */
hold_position = i 1;
} else
substring_span ;
/* checking our character at the end of the loop,
* and incrementing after the fact,
* let's us include the null terminating character as a delimiter,
* as we will only fail to enter the loop after processing it
*/
} while (name[i ] != '\0');
for (size_t i = 0; i < length_of_copies; i )
printf("%zu: [%s]\n", i 1, copies[i]);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/407557.html
標籤:
下一篇:動態分配二維陣列的最佳使用
