⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 05章 指针与字符串.txt

📁 C++大学教程txt版中文版 C++大学教程txt版中文版
💻 TXT
📖 第 1 页 / 共 5 页
字号:
14  for(int i = 0; i < 4; i++ ),
15     cou << "b[ " << i <<  "] = << b[ i ] << '\n';
16
17
18   cout << "\nPointer/offset notation where\n"
19       << "the pointer is the array name\n";
2O
21   for (int offset = 0; offset < 4; offset++ )
22      cout << "* (b +" << offset << ") ="
23           << *( b + offset ) << '\n';
24
25
26   cout << "\nPointer subscript notation\n";
28   for ( i = 0; i < 4; i++ )
29     cout << "bPtr[" << i << "] =" << bPtr[ i ] << '\n';
31   cout << "\nPointer/offset notation\n";
32
33   for ( offset = 0; offset < 4; offset++ )
34     cout << "*(bPtr + "<< offset << ") ="
35          << * ( bPtr + offset ) << '\ n';
36
37   return 0;
38 }

输出结果:
Array b Printed with:
Array subscript notation
Pointer/offset notation where
the pointer is the array name
* (b + 0) = 10
* (b + 1) = 20
* (b + 2) = 30
* (b + 3) = 40
Pointer subscript notation
bPtr[ 0 ] = 10
bPtr[ 1 ] = 20
bPtr[ 2 ] = 30
bPtr{ 3 ] = 40
Pointer/offset notation
*(bPtr + 0) = 10
*(bPtr + 1) = 20
*(bPtr + 2) = 30
*(bPtr + 2) = 40

                         图5.20  用我们介绍的四种方法引用数组元素

1 // Fig. 5.21: figOS_21.cpp
2 // Copying a string using array notation
3 // and pointer notation.
4 #include <iostream.h>
5
6 void copy1( char *, const char * );
7 void copy2( char *, const char * );
8
9  int main()
10 {
11
12       string3[ 10 ], string4[] = "Good Bye";
13
14   copy1( string1, string2 );
15   cout << "string1 =" << string1 << endl;
16
17   copy2( string3, string4 );
18   cout << "string3 = "<< string3 << endl';
19
20   return 0;
21 }
22
23 // copy s2 to sl using array notation
24 void copy1( char *s1, const char *s2 )
25 {
26   for ( int i = 0; ( s1[ i ] = s2[ i ] ) != '\0'; i++ )
27     ;  // do nothing in body
28 }
29
30 // copy s2 to sl using pointer notation
31 void copy2( char *s1, const char *s2 )
32 {
33   for ( ; ( *s1 = *s2 ) != '\0'; s1++, s2++ )
34      ;  // do nothing in body
35 }

输出结果:
string1 = Hello
string3 = Good Bye

                                图5.21  使用数组和指针符号复制字符串

    函数copy1用数组下标符号将s2中的字符串复制到字符数组s1中。函数声明一个作为数组下标的整型计数器变量i。for结构的首部进行整个复制操作,而for结构体本身是个空结构。首部中指定i初始化为0,并在每次循环时加1。for的条件“(s1[i]=s2[i])!='\0',从s2向s1一次一个字符地进行复制操作。遇到s2中的null终止符时,将其赋给s1,循环终止,因为null终止符等于'\0'。
    记住.赋值语句的值是赋给左边参数的值。
    函数copy2用指针和指针算法将s2中的字符串复制到s1字符数组。同样是在for结构的首部进行整个复制操作.首部没有任何变量初始化。和copy1中一样,条件(*s1=*s1)!='\0'进行复制操作。
复引用指针s2,产生的字符赋给复引用的指针s1。进行条件中的赋值之后,指针分别移到指向s1数组的下一个元素和字符串s2的下一个字符。遇到s2中的null终止符时,将其赋给s1,循环终止。
    注意copy1和copy2的第一个参数应当是足够大的数组,应能放下第二个参数中的字符串,否则可能会在写人数组边界以外的内存地址时发生错误。另外,注意每个函数中的第二个参数声明为const char*(常量字符串)。在两个函数中,第二个参数都复制到第一个参数,一次一个地从第二个参数复制字符,但不对字符做任何修改。因此,第二个参数声明为常量值的指针,实施最低权限原则。两个函数都不需要修改第二个参数,因此不向这两个函数提供修改第二个参数的功能。

5.9  指针数组
    数组可以包含指针,这种数据结构的常见用法是构成字符串数组,通常称为字符串数组(stringarray)。字符串数组中的每项都是字符串,但在C++中,字符串实际上是第一个字符的指针。因此,字符串数组中的每项实际上是字符串中第一个字符的指针。下列字符串数组suit的声明可以表示一副牌:
    char‘*suit[ 4 ] = {  "Hearts","Diamonds","Clubs","Spades"};
声明的suit[4]部分表示4个元素的数组。声明的char*部分表示数组suit的每个元素是char类型的指针。数组中的4个值为”Hearts'’、”Diamonds”、”Clubs”和”Spades”。每个值在内存中存放成比引号中的字符数多一个字符的null终上字符串。4个字符串长度分别为7、9、6、7。尽管这些字符串好像是放在suil数组中,其实数组中只存放指针(如图5.22)。每个指针指向对应字符串中的第一个字符。这样,尽管:suit数组是定长的,但可以访问任意长度的字符串,这是C++强大的数据结构功能所带来的灵活性。
    



                                 图5. 22  suit数组的图形表示

    suit字符串可以放在双下标数组中,每一行表示一个suit,每一列表示suit名的第一个字符、这种数据结构每一行应有固定列数,能够放下最长的字符串。因此,存放大量字符串而大部分字符串长度均比最长字符串短许多时,可能浪费很多内存空间。我们将在下一节用字符串数组帮助整理一副牌。

5.10  实例研究:洗牌与发牌
    本节用随机数产生器开发一个洗牌与发牌程序。这个程序可以用于实现玩某种牌的游戏程序。
    为了解决一些微妙的性能问题,我们故意用次优洗牌与发牌算法。练习中要开发更有效的算法。
    利用自上而下逐步完善的方法,我们开发一个程序,洗52张牌并发52张牌。自上而下逐步完善的方法在解决大而复杂的问题时特别有用。
    我们用4 x 13的双下标数组deck表示要玩的牌(如图5.23)。行表示花色,0表示红心,1表示方块,2表示梅花,3表示黑桃。列表示牌的面值,0到9对应A到10,10到12对应J、Q、K。我们要装入字符串数组suit,用字符串表示4个花色,用字符串数组face的字符串表示13张牌的面值。
    这堆牌可以进行如下的洗牌:首先将数组deck清空,然后随机选择row(0--3)和column(0—12)。将数字插入数组元素deck[row][column](表示这个牌是洗出的牌中要发的第一张牌)。继续这个过程,在deck数组中随机插入数字2、3、…52,表示洗出的牌中要发的第二、三、…、五十二张牌。在deck数组填上牌号时,一张牌可能选择两次,即选择的时候deck[row][column]为非0值。
    忽略这个选择,随机重复选择其他row和colunm,直到找出未选择的牌。最后,在52个deck元素中插入1到52的值。这时,就完全洗好了牌。



                  
                                图5.23  双下标数组deck表示要玩的牌

  这个洗牌算法在随机重复选择已经洗过的牌时可能需要无限长的时间。这种现象称为无穷延迟(indefinite postponement)。练习中将介绍更好的洗牌算法,消陈无穷延迟。

    性能提示5.3
    有时自然方式的算法可能包含无穷延迟等微妙的性能问题,应寻找能避免无穷延迟的算法。

    要发第一张牌,我们要寻找匹配1的deck[row][column]元素,这是用嵌套for结构进行的,n,w取。到3t column取。到12。这个数组元素对应哪种牌呢?suit数组预先装入了四种花色,因此要取花色,只要打印字符串suit[row];同样,要取牌值,只要打印字符串face[column]还要打印字符串”of",按正确的顺序打印,即可得到每张牌如”King of Clubs"、”Ace of Diamonds',等等。
    下面用自上而下逐步完善的方法进行。顶层为:
    Shuffle and deal 52 cards

第一步完善结果为:
   Initialize the suit array
   Initialize the face array
   Initialize the deck array
   Shuffle the deck
   Deal 52 cards
”Shumelhedeck”可以展开成:
      For each of the 52 cards
      Place card number in randomly selected unoccupied slot of deck
"Deal 52 cards" 可以展开成:
      For each of the 52 cards
      Find card number in deck array and print face and suit of card

合在一起得到第二步完善结果为:
  Initialize the suit array
  Initialize the face array
  Initialize the deck array
  For each of the 52 cards
     Place card number in randomly selected unoccupied slot of deck
  For each of the 52 cards
     Find card number in deck array and print face and suit of card
"Place eard numberin radomly selected unoccupied slot of deck" 可以展开成:
 Choose slot of deck randomly

 While chosen slot of deck has been previously chosen
 Choose slot of deck randomly
 Place card number in chosen slot of deck
"Find card numberin deck array and printface and suit of card" 可以展开成:
    For each slot of the deck array
    If slot contains card number
        Print the face and suit of the card

合在一起得到第三步完善结果为:
Initialize the suit array
Initialize the face array
Initialize the deck array
For each of the 52 cards
       Choose slot of deck randomly
While slot of deck has been previously chosen
       Choose slot of deck randomly
       Place card number in chosen slot of deck
For each of the 52 cards
       For each slot of deck array
             If slot contains desired card number
            Print the face and suit of the card

    这样就完成了完善过程。注意,如果将洗牌与发牌算法组合成每张牌在放到牌堆上时进行发牌,则这个程序能更加有效。我们选择分别编程这些操作,因为通常是先洗后发,而不是边洗边发。
    图5.24显示了洗牌与发牌程序,图5.25显示了示例执行结果。注意函数deal中使用的输出格式:
cout << setw( 5 ) << setiosflags( ios::right )
        << wFace[ column ] << "of"
        << setw( 8 ) << setiosflags( ios::left )
        << wSuit[ row ]
        << (card % 2 ==0? '\n': '\t');
上述输出语句使牌的面值在5个字符的域中右对齐输出,而花色在8个字符的域中左对齐输出。输出打印成两列格式。如果输出的牌在第一列,则在后面输出一个制表符,移到第二列,否则输出换行符。

1 // Fig. 5.24: fig05_24.cpp
2 // Card shuffling and dealing program
3 #include <iostream.h>
4 #include <iomanip.h>
5 #include <stdlib.h>
6 #include <time.h>
7
8 void shuffle( iht [][ 13 ] );
9 void deal( const int [][ 13 ], const char *[], const char *[] );
10
11 int main()
12 {
13   const char * suit[ 4 ] =
14     { "Hearts", "Diamonds", "Clubs", "Spades" };
15   const char * face[ 13 ] =
16     { "Ace", "Deuce", "Three", "Four",
17       "Five", "Six", "Seven", "Eight",
18       "Nine", "Ten", "Jack", "Queen", "King" };
19   int deck[ 4 ][ 13 ] = { 0 } ;
20
21   srand( time( 0 ) );
22
23   shuffle( deck );
24   deal( deck, face, suit ):
25
26   return 0;
27 }
28
29 void shuffle( int wDeck[ ][ 13 ] )
30 {
31   int row, column;
32
33   for(int card = 1; card <= 52; card++ ) {
34     do{ 
35        row = rand() % 4;
36        column = rand() % 13;
37     } while( wDeck[ row ][ column ] != 0 );
38
39     wDeck[ row ][ columo ] = card;
40   }
41 }
42
43 void deal( const int wDeck[ ][ 13 ], const char * wFace[ ] ,
44         const char *wSuit[] )
45 {
46   for (int card = 1; card <= 52; card++ )
47
48     for ( int row = 0; row <= 3; row++ )
49

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -