(原創) 如何將輸入的字串存到記憶體後,再一起印出來? (C/C++) (C)

Abstracion
最近有同學問我這個問題,整理之後,將心得提出與大家分享。

Introduction
學習C/C++時,一開始要學的就是標準輸入輸出。一個很簡單的需求:由鍵盤輸入字串存入記憶體,若輸入了quit,則結束,最後在全部已經存在記憶體中的字串。先看看C++要怎麼寫。

C++ / string_into_svec.cpp

1 /* 
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3 
4 Filename    : string_into_svec.cpp
5 Compiler    : Visual C++ 8.0
6 Description : Demo how to use string & vector in C++
7 Release     : 03/21/2008 1.0
8 */
9 #include <iostream>
10 #include <string>
11 #include <vector>
12 
13 using namespace std;
14 
15 int main() {
16   string s;
17   vector<string> svec;
18  
19   while(1) {
20     cin >> s;
21     if (s == "quit")
22       break;
23      
24     svec.push_back(s);
25   }
26  
27   vector<string>::iterator iter = svec.begin();
28   for(; iter != svec.end(); ++iter)
29     cout << *iter << endl;
30 }


執行結果

Hello World
Hello C++
quit
Hello
World
Hello
C++


既然用C++,當然使用較好用的std::string與std::vector,一切程式都很直觀,不過既然要用STL,若能用std::copy()就更精簡了。

C++ / string_into_svec_stl.cpp

1 /* 
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3 
4 Filename    : string_into_svec_stl.cpp
5 Compiler    : Visual C++ 8.0
6 Description : Demo how to use string & vector in C++ & STL
7 Release     : 03/21/2008 1.0
8 */
9 #include <iostream>
10 #include <string>
11 #include <vector>
12 #include <algorithm>
13 
14 using namespace std;
15 
16 int main() {
17   string s;
18   vector<string> svec;
19  
20   while(1) {
21     cin >> s;
22     if (s == "quit")
23       break;
24      
25     svec.push_back(s); 
26   }
27  
28   copy(svec.begin(), svec.end(), ostream_iterator<string>(cout, "\n"));
29 }


執行結果

Hello World
Hello C++
quit
Hello
World
Hello
C++


使用std::copy()後,仍發現一個缺憾,由於cin在擷取字串時,把空格當成分割字串的方式,所以明明輸入的是Hello World一個字串,但卻變成Hello和World兩個字串,所以打算改用getchar(),這樣就能把空格也當成字串的一部分。

C++ / string_into_svec_space.cpp

1 /* 
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3 
4 Filename    : string_into_svec_space.cpp
5 Compiler    : Visual C++ 8.0
6 Description : Demo how to use string & vector in C++ with space
7 Release     : 03/21/2008 1.0
8 */
9 #include <iostream>
10 #include <string>
11 #include <vector>
12 #include <stdio.h>
13 #include <algorithm>
14 
15 using namespace std;
16 
17 int main() {
18   string s;
19   vector<string> svec;
20   char ch;
21  
22   while(1) {
23     while((ch = getchar()) != '\n') {
24       s.push_back(ch);
25     }
26    
27     if (s == "quit")
28       break;
29    
30     svec.push_back(s);
31      
32     s = "";
33   }
34  
35   copy(svec.begin(), svec.end(), ostream_iterator<string>(cout, "\n"));
36 }


執行結果

Hello World
Hello C++
quit
Hello World
Hello C++


使用getchar()後,執行結果如預期,是一個完美的版本。若用純C語言呢?std::string必須改用char [],std::vector必須改成array。

C語言 / string_into_array_space.c

 1 /* 
 2 (C) OOMusou 2008 http://oomusou.cnblogs.com
 3 
 4 Filename    : string_into_array_space.c
 5 Compiler    : Visual C++ 8.0
 6 Description : Demo how to use char[] & array in C with space
 7 Release     : 03/21/2008 1.0
 8 */
 9 #include <stdio.h>
10 #include <string.h>
11 
12 #define SLEN 255
13 #define ALEN 255
14 
15 int main() {
16   char s[SLEN];
17   char sarr[ALEN][SLEN];
18   char ch;
19   int j = 0;
20   int k = 0;
21   
22   while(1) {
23     int i = 0;
24     while((ch = getchar()) != '\n')
25       s[i++= ch;
26  
27     s[i] = '\0';   
28     
29     if (!strcmp(s, "quit"))
30       break;
31     else {
32       strncpy(sarr[j], s, SLEN - 1);
33       sarr[j++][SLEN - 1= '\0';
34     }
35   }
36   
37   while(k < j)
38     printf("%s\n", sarr[k++]);
39 }

執行結果

Hello World
Hello C++
quit
Hello World
Hello C++


用C語言改寫後,第一個衝擊就是C語言的字串,C的字串是pointer概念,和其他語言完全不同,還必須配合strcmp()、strncpy()、memset()等函數才能正常運作。C語言最大的特色在於,pointer貫穿整個語言,什麼都可以用pointer寫,既然『你要東方不敗,我就給你東方不敗』,整個程式全部用pointer改寫。

C語言 / string_into_array_space_pointer.c

 1 /* 
 2 (C) OOMusou 2008 http://oomusou.cnblogs.com
 3 
 4 Filename    : string_into_array_space_pointer.c
 5 Compiler    : Visual C++ 8.0
 6 Description : Demo how to use char[] & array in C with space by pointer
 7 Release     : 03/21/2008 1.0
 8 */
 9 #include <stdio.h>
10 #include <string.h>
11 
12 #define SLEN 255
13 #define ALEN 255
14 
15 int main() {
16   char s[SLEN];
17   char sarr[ALEN][SLEN];
18   char ch;
19   int j = 0;
20   int k = 0;
21   
22   while(1) {
23     int i = 0;
24     while((ch = getchar()) != '\n')
25       *(s+i++= ch;
26     
27     *(s+i) = '\0';
28     
29     if (!strcmp(s, "quit"))
30       break;
31     else {
32       strncpy(*(sarr + j), s, SLEN - 1);
33       *(*(sarr + (j++)) + SLEN - 1= '\0';
34     }
35   }
36   
37   while(k < j)
38     printf("%s\n"*(sarr+k++));
39 }

執行結果

Hello World
Hello C++
quit
Hello World
Hello C++


全用pointer改寫後,若不熟悉C語言風格的人,一定認為這是天書,其實這只是習慣問題,改用pointer的思考方式,多寫就習慣了。

不過由此也看到C語言這種靜態語言的本質:『array得事先宣告好大小』,而不能如std::vector那樣有彈性,只要不斷的push_back()就好,C語言要如何才能如std::vector那樣有彈性呢?C語言若要動態,只能靠malloc(),但不是用malloc()來宣告array,而是用malloc()來搭配linked list。

C語言

1 /* 
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3 
4 Filename    : string_into_linkedlist_space.c
5 Compiler    : Visual C++ 8.0
6 Description : Demo how to use char[] & linkedlist in C with space
7 Release     : 03/21/2008 1.0
8 */
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 
13 #define SLEN 255
14 
15 struct list {
16   char s[SLEN];
17   struct list *next;
18 };
19 
20 int main() {
21   char s[SLEN];
22   char ch;
23   struct list *head    = NULL;
24   struct list *current = NULL;
25   struct list *prev    = NULL;
26  
27   while(1) {
28     int i = 0;
29     while((ch = getchar()) != '\n')
30       *(s+i++) = ch;
31    
32     *(s+i) = '\0';
33    
34     if (!strcmp(s, "quit"))
35       break;
36    
37     current = (struct list *)malloc(sizeof(struct list));
38     if (current == NULL)
39       exit(EXIT_FAILURE);
40        
41     current->next = NULL;
42      
43     strncpy(current->s, s , SLEN -1);
44     current->s[SLEN -1] = '\0';
45      
46     if (head == NULL)
47       head = current;
48     else
49       prev->next = current;
50        
51     prev = current;
52     memset(s, '\0', sizeof(s));
53   }
54  
55   // display linked list
56   current = head;
57   while(current != NULL) {
58     printf("%s\n", current->s);
59     current = current->next;
60   }
61  
62   // free linked list
63   current = head;
64   while(current != NULL) {
65     prev = current;
66     current = current->next;
67     free(prev);
68   }
69 }

執行結果

Hello World
Hello C++
quit
Hello World
Hello C++


改用linked list後,就和std::vector一樣有彈性了,缺點就是程式碼稍長了些。

Conclusion
有很長一段時間,我一直很懷疑學資料結構要幹嘛?也聽到很多專業工程師,不會資料結構一樣表現的很好,問題出在哪裡呢?原來現在大部分的語言,library和framework都相當完善,都已經將常用的資料結構和演算法內建了,所以你只要會用就好,根本不需自己重新創造輪子,如C++有STL,C#有.NET Framework,Java有J2SE...等,但若你用的是C語言,由於標準library有限,所以資料結構就顯得非常重要,在上例可以看出,C語言沒有C++的std::vector可用,只好拿出資料結構的看家本領linked list了。所以資料結構並非不重要,只是現在語言越來越高階,library越來越齊全,導致實務上使用資料結構的機會越來越少,但若你今天只有C語言可用(如8051、嵌入式系統、Linux kernel)時,這就是資料結構發光發熱的地方了。

See Also
(原創) 簡單的Linked List實現 (C/C++) (Data Structure)
(原創) C/C++哪些地方會用到pointer呢? (C/C+) (C)

posted on 2008-03-21 16:59  真 OO无双  阅读(9954)  评论(6编辑  收藏  举报

导航