📄 04章 数组.txt
字号:
4 #include <iomanip.h>
5
6 int main()
7
8 const int responseSize = 40, frequencySize = 11;
9 int responses[ responseSize I = { 1, 2, 6, 4, 8, 5, 9, 7, 8,
10 10, 1, 6, 3, 8, 6, 10, 3, 8, 2, 7, 6, 5, 7, 6, 8, 6, 7,
11 5, 6, 6, 5, 6, 7, 5, 6, 4, 8, 6, 8, 10 ];
12 int frequency [ frequencySize ] = { 0 };
13
14 for (int answer = 0; answer < responseSize; answer++ )
15 ++frequency [ responses[ answer ] ];
16
17 cout << "Rating" << setw( 17 ) << "Frequency" << endl;
18
19 for ( int rating = 1; rating < frequencySize; rating++ )
20 cout << setw( 6 ) << rating
21 << setw( 17 ) << frequency [ rating ] << endl;
22
23 return 0;
24 }
输出结果:
Rating Frequency
1 2
2 2
3 2
4 2
5 5
6 11
7 5
8 7
9 l
10 3
图4.9 学生调查分析程序
编程技巧4.2
努力保证程序的清晰性,有时甚至可以为保证程序清晰而牺牲内存与处理器时间的使用效率。
性能堤示4.2
有时性能考虑比保证程序的清晰性更重要。
第一个for循环一次一个地从responses数组取得回答,并将frequeney数组中的10个计数器(frequency[l]到frequency[10])之一加1。这个循环中的关键语句如下:
++frequency[ responses[answer>;
这个语句根据responses[answer]的值相应递增frequency计数器。例如,计数器answer为0时,responses[answer]为1,因此“++frequency[responses[answer>;”实际解释如下:
++frequency[1];
将数组下标为1的元素加1。计数器answer为1时,responses[answer]为2,因此"++frqueney[responses[answer>;”实际解释如下:
++frequency[2];
将数组下标为2的元素加1。计数器answer为2时,response[answer]为6,因此"++frequency[responses
[answer>;”实际解释如下:
++frequency[6];
将数组下标为6的元素加1等等。注意,不管调查中处理多少个回答,都只需要11个元素的数组(忽略元素0)即可汇总结果。如果数据中包含13之类的无效值,则程序对frequency[13]加1,在数组边界之外。C++没有数组边界检查,无法阻止计算机到引用不存在的元素。这样执行程序可能超出数组边界,而不产生任伺警告。程序员应保证所有数组引用都在数组边界之内。C++是个可扩展语言,第8章介绍扩展C++,用类将数组实现为用户自定义类型。新的数组定义能进行许多C++内建数组中没有的操作,例如,可以直接比较数组,将一个数组赋给另一数组,用cin和cout输入和输出整个数组,自动初始化数组,防止访问超界数组元素和改变下标范围(甚至改变下标类型),使数组第一个元素下标不一定为0。
常见编程错误4.6
引用超出数组边界的元素是个执行时的逻辑错误,而不是语法错误。
测试与调试提示4.1
对数组进行循环操作时,数组下标不能低于0,不能高于数组中的元素总数(应比数组中的元素总数少1)。保证避免循环终止条件访问超界数组元素。
测试与调试提示4.2
程序应验证所有输入值的正确性,防止错误信息影响程序计算。
可移植性提示4. 1
访问超界数组元素所带来的影响(通常很严重)是与系统有关。
测试与调试提示4.3
第6章开始介绍类时,将介绍如何开发“智能”数组,可以在运行时自动检查所有下标引用在边界之内。使用这种智能数组能消除一定的缺陷。
下一个例子(图4.10)从数组读取数值,并用条形图或直方图进行描述。打印每个数值,然后在数字旁边打印该数字所指定星号个数的条形图。嵌套for循环实现绘制条形图。注意用endl结束直方图。
常见编程错误4.7
尽管一个for循环和另一个嵌套for循环中可以用相同计数器变量,但这通常是个逻辑错误。
1 // Fig. 4.10: fig04_10.cpp
2 // Histogram printing program
3 #include <iostream.h>
4 #include <iomanip.h>
5
6 int main()
7 {
8 const int arraySize = 10;
9 int n[ arraySize ] = { 19, 3, 15, 7, 11, 9, 13, 5, 17, 1 };
10
11 cout << "Element" << setw( 13 ) << "Value"
12 << setw( 17 ) << "Histogram" << endl;
13
14 for (int i = 0; i < arraySize ; i++ ) {
15 cout << setw( 7 ) << i << setw( 13 )
16 << n[ i ] << setw( 9 );
17
18 for ( int j = 0; j < n[ i ] ; j++ ) // print one bar
19 cout << '*';
20
21 cout << endl;
22 }
23
24 return 0;
25 }
输出结果:
Element Value Histogram
0 19 *******************
1 3 ***
2 15 ***************
3 7 *******
4 11 ************
$ 9 *********
6 13 *************
7 5 ******
8 17 *****************
9 1
测试与调试提示4.4
尽管for循环中的计数器变量可以修改,但这样容易造成一些缺陷,应当避免。
第3章介绍了投骰子程序(参见图3.8),就是将六面体骰子投6000次,测试随机数产生器是否真的产生随机数。图4.11显示了这个程序的数组版本。
1 // Fig. 4.11: figo411.cpp
2 // Roll a six-sided die 6000 times
3 #include <iostream,h>
4 #include <iomanip.h>
5 #include <stdlib.h>
6 #include <time.h>
7
8 int main()
9 {
10 const iht arraySize = 7;
11 int face, frequency[ arraySize ] = { 0 };
12
13 stand( time( 0 ) );
14
10 for ( int roll =1; roll <= 6000; roll++ )
16 ++frequency[ 1 + rand() % 6 ]; // replaces 20-line switch
17 // of Fig. 3.8
18
19 cout << "Face" << setw( 13 ) << "Frequency" << endl;
20
21 // ignore element 0 in the frequency array
22 for ( face = 1; face < arraySize ; face++ )
23 cout << setw{ 4 ) << face
24 << setw( 13 ) << frequency[ face ] << endl;
25
26 return 0;
27 }
输出结果:
Face Frequency
1 1037
2 987
3 1013
4 1028
5 952
6 983
图4.ll 用数组而不用switch结构设计投骰子程序
前面只介绍了整型数组,但数组可以是任何类型,下面要介绍如何用字符数组存放字符串。前面介绍的字符串处理功能只有用cout和<<输入字符串,“hello'’之类的字符串其实就是一个字符数组。字符数组有几个特性。
字符数组可以用字符串直接量初始化。例如,下列声明:
char string1[] = "first";
将数组string1的元素初始化为字符串"first"中的各个元索。上述声明中string1的长度是编译器根据字符串长度确定的。注意,字符串“first”包括五个字符加一个特殊字符串终止符,称为空字符(null character),这样,字符串string1实际上包含6个元素。空字符对应的字符常量为'\0'(反斜杠加0),所有字符串均用这个空字符结尾。表示字符串的字符数组应该足以放置字符中中的所有字符和空字符。
字符数组还可以用初始化值列表中的各个字符常量初始化。上述语句也可以写成:
char string1[] ={ 'f', 'i', 'r', 's', 't', '\0' }
由于字符串其实是字符数组,因此可以用数组下标符号直接访问字符串中的各个字符。例如,string1[O]是字符'f',string1[3]是字符's'。
我们还可以用cin和>>直接从键盘输入字符数组。例如,下列声明:
char string2[20];
生成的字符数组能存放19个字符和一个null终止符的字符串。
下列语句:
cin>>string2;
从键盘中将字符串读取到string2中。注意,上述语句中只提供了数组名,没有提供数组长度的信息。程序员要负责保证接收字符串的数组能够放置用户从键盘输入的任何字符串。cin从键盘读取字符,直到遇到第一个空白字符,它不管数组长度如何。这样,用cin和>>输人数据可能插入到数组边界之外(5.12节介绍如何防止插入到数组边界之外)。
常见编程错误4.8
如果cin>>提供足够大的数组,则键盘输入时可能造成数据丢失和其他严重的运行时错误。
可以用cout和<<输出表示空字符终止字符串的字符数组。下列语句打印数组string2:
cout<<string2<<endl;
注意cout<<和cin>>一样不在乎字符数组的长度。一直打印字符串的字符,直到遇到null终止符为止。
图4.12演示直接用字符串初始化字符数组、将字符串读取到字符数组中、将字符数组作为字符串打印以及访问字符串的各个字符。
1 // Fig. 4_12: fig04_12.cpp
2 // Treating character arrays as strings
3 #include <iostream.h>
4
5 int main()
6 {
7 char string1[ 20 ], string2[] = "string literal";
8
9 cout << "Enter a string: ";
10 cin >> string1;
11 cout << "string1 is: "<< string1
12 << "\nstring2 is: " << string2
13 << "stringl with spaces between characters is:\n";
14
15 for ( int i = 0; string1[ i ] != '\0'; i++ )
16 cout << string1[ i ] << ' ';
17
18 cin >> stringl; // reads "there"
19 cout << "\nstring1 is: "<< string1 << endl;
2O
21 cout << endl;
22 return 0;
23 }
输出结果:
Enter a string: Hello there
string1 is: Hello
string2 is: string literal
string1 with spaces between characters is:
Hello
string1 is: there
图4.12将字符数组当作字符串
图4.12用for结构在string1数组中循环,并打印各个字符,用空格分开。for结构的条件string1[i]!='\0'在遇到字符串的null终止符之前一直为真。
第3章介绍了存储类说明符static。函数体中的static局部变量在程序执行期间存在,但只能在函数体中访问该变量。
性能提示4.3
可以用static对局部数组声明,使该数组不必在每次调用函数时生成和初始化,该数组在程序中每次退出函数时不会删除,这样可以提高性能。
声明为static的数组在程序装入时初始化。如果程序员不把static数组显式初始化,则编译器将这个数组初始化为0。
图4.13显示了带有声明为static的局部效组的staticArrayInit函数和带自动局部数组的automaticArrayInit函数。staticArrayInit调用两次,编译器将static局部数组初始化为0。该函数打印数组,将每个元素加5,然后再次打印该数组;函数第二次调用时.static数组包含第一次调用时所存放的值。函数automaticArrayInit也调用两次,但自动局部数组的元素用数值1、2、3初始化。该函数打印数组,将每个元素加5,然后再次打印该数组;函数第二次调用时,数组重新初始化为1、2、3,因为这个数组是自动存储类。
1 // Fig. 4.13: fig0413.cpp
2 // Static arrays are initialized to zero
3 #include <iostream.h>
4
5 void staticArrayInit( void );
6 void automaticArrayInit( void );
7
8 int main()
9 {
10 cout << "First call to each function:\n";
11 staticArrayInit();
12 automaticArrayInit();
13
14 cout << "\n\nSecond call to each function:\n";
15 staticArrayInit();
16 automaticArrayInit();
17 cout << endl;
18
19 return 0;
2O
21
22 // function to demonstrate a static local array
23 void staticArrayInit( void )
24 {
25 static iht arrayl[ 3 ] ;
26 int i;
27
28 cout << "\nValues on entering staticArrayInit:\n";
29
30 for ( i = 0; i < 3; i++ )
31 cout << "array1[ " << i << " ] =" << array1[ i ] << " ";
32
33 cout << "\nValues on exiting staticArrayInit:\n";
34
35 for ( i = 0; i < 3; i++ )
36 cout << "array1[ " << i << " ] = "
37 << ( arrayl[ i ] += 5 ) <<" ";
38 }
39
40 // function to demonstrate an automatic local array
41 void automaticArrayInit( void )
42 {
43 int i, array2[ 3 ] = { 1, 2, 3 };
44
45 cout << "\n\nValues on entering automaticArrayInit:\n";
46
47 for ( i = 0; i < 3; i++ )
48 cout << "array2[ "<< i << "] = "<< array2[ i ] << " " ;
49
50 cout << "\nValues on exiting automaticArrayInit:\n";
51
52 for ( i = 0; i < 3; i++ }
53 cout << "array2[ "<< i << "] ="
54 << ( array2[ i ] += 5 ) << " " ;
55 }
输出结果:
First call to each function:
Values on entering staticArrayInit:
arrayl[ 0 ] = 0 array1[ 1 I = 0 array1[ 2 ] = 0
Values on exiting staticArrayInit:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -