(原創) 如何將輸入的字串存到記憶體後,再一起印出來? (C/C++) (C)
Abstracion
最近有同學問我這個問題,整理之後,將心得提出與大家分享。
Introduction
學習C/C++時,一開始要學的就是標準輸入輸出。一個很簡單的需求:由鍵盤輸入字串存入記憶體,若輸入了quit,則結束,最後在全部已經存在記憶體中的字串。先看看C++要怎麼寫。
C++ / string_into_svec.cpp
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 C++
quit
Hello
World
Hello
C++
既然用C++,當然使用較好用的std::string與std::vector,一切程式都很直觀,不過既然要用STL,若能用std::copy()就更精簡了。
C++ / string_into_svec_stl.cpp
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 C++
quit
Hello
World
Hello
C++
使用std::copy()後,仍發現一個缺憾,由於cin在擷取字串時,把空格當成分割字串的方式,所以明明輸入的是Hello World一個字串,但卻變成Hello和World兩個字串,所以打算改用getchar(),這樣就能把空格也當成字串的一部分。
C++ / string_into_svec_space.cpp
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 C++
quit
Hello World
Hello C++
使用getchar()後,執行結果如預期,是一個完美的版本。若用純C語言呢?std::string必須改用char [],std::vector必須改成array。
C語言 / string_into_array_space.c
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 C++
quit
Hello World
Hello C++
用C語言改寫後,第一個衝擊就是C語言的字串,C的字串是pointer概念,和其他語言完全不同,還必須配合strcmp()、strncpy()、memset()等函數才能正常運作。C語言最大的特色在於,pointer貫穿整個語言,什麼都可以用pointer寫,既然『你要東方不敗,我就給你東方不敗』,整個程式全部用pointer改寫。
C語言 / string_into_array_space_pointer.c
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 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語言
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 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)