資料結構

資料結構

資料結構是聚合在一個名稱下的資料元素的集合。這些被稱為成員的資料元素可以有不同的型別和不同的長度。在 C++ 中,可以使用以下語法宣告資料結構:

struct type_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
.
.
} object_names;

其中 type_name 是結構型別的名稱,object_name 可以是一組該結構型別的物件的有效識別符號。在花括號 {} 內,是資料成員的列表,每個成員都用一個型別和一個有效的識別符號作為其名稱來指定。

例如:

1
2
3
4
5
6
7
struct product {
  int weight;
  double price;
} ;

product apple;
product banana, melon;

這裡聲明瞭一個名為 product 的結構型別,並定義它有兩個成員:weightprice,每個成員都有不同的基本型別。這個宣告建立了一個新的型別(product),然後用它來宣告該型別的三個物件(變數):applebananamelon。請注意,一旦 product 被宣告,它的使用方式就和任何其他型別一樣。

struct 定義的末尾,分號(;)之前,可以使用可選欄位 object_names 來直接宣告該結構型別的物件。例如,結構物件 applebananamelon 可以在定義資料結構型別時就進行宣告:

1
2
3
4
struct product {
  int weight;
  double price;
} apple, banana, melon;

在這種情況下,如果指定了 object_names,型別名稱(product)就變成了可選的:struct 語句要麼需要一個 type_name,要麼在 object_names 中至少有一個名稱,但不一定兩者都需要。

很重要的一點是,要清楚地區分結構型別名稱(product)和該型別的物件(applebananamelon)。從一個結構型別(product)可以宣告出許多物件(例如 applebananamelon)。

一旦聲明瞭某個特定結構型別的三個物件(applebananamelon),就可以直接訪問它們的成員。語法很簡單,在物件名和成員名之間插入一個點(.)。例如,我們可以像操作它們各自型別的標準變數一樣來操作這些元素:

1
2
3
4
5
6
apple.weight
apple.price
banana.weight
banana.price
melon.weight
melon.

它們每一個的資料型別都與其所引用的成員相對應:apple.weightbanana.weightmelon.weight 的型別是 int,而 apple.pricebanana.pricemelon.price 的型別是 double

這裡是一個結構型別在實際應用中的真例項子:

// example about structures
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

struct movies_t {
  string title;
  int year;
} mine, yours;

void printmovie (movies_t movie);

int main ()
{
  string mystr;

  mine.title = "2001 A Space Odyssey";
  mine.year = 1968;

  cout << "Enter title: ";
  getline (cin,yours.title);
  cout << "Enter year: ";
  getline (cin,mystr);
  stringstream(mystr) >> yours.year;

  cout << "My favorite movie is:\n ";
  printmovie (mine);
  cout << "And yours is:\n ";
  printmovie (yours);
  return 0;
}

void printmovie (movies_t movie)
{
  cout << movie.title;
  cout << " (" << movie.year << ")\n";
}
Enter title: Alien
Enter year: 1979

My favorite movie is:
 2001 A Space Odyssey (1968)
And yours is:
 Alien (1979)

這個例子展示了物件的成員如何像普通變數一樣工作。例如,成員 yours.year 是一個 int 型別的有效變數,而 mine.title 是一個 string 型別的有效變數。

但物件 mineyours 本身也是有型別的變數(movies_t 型別)。例如,它們都被傳遞給了 printmovie 函式,就像它們是簡單變數一樣。因此,資料結構的一個特性就是能夠既可以單獨引用其成員,也可以引用整個結構。在這兩種情況下,都使用相同的識別符號:結構體的名稱。

因為結構體是型別,所以它們也可以用作陣列的型別,從而構建它們的表格或資料庫:

// array of structures
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

struct movies_t {
  string title;
  int year;
} films [3];

void printmovie (movies_t movie);

int main ()
{
  string mystr;
  int n;

  for (n=0; n<3; n++)
  {
    cout << "Enter title: ";
    getline (cin,films[n].title);
    cout << "Enter year: ";
    getline (cin,mystr);
    stringstream(mystr) >> films[n].year;
  }

  cout << "\nYou have entered these movies:\n";
  for (n=0; n<3; n++)
    printmovie (films[n]);
  return 0;
}

void printmovie (movies_t movie)
{
  cout << movie.title;
  cout << " (" << movie.year << ")\n";
}
Enter title: Blade Runner
Enter year: 1982
Enter title: The Matrix
Enter year: 1999
Enter title: Taxi Driver
Enter year: 1976
 
You have entered these movies:
Blade Runner (1982)
The Matrix (1999)
Taxi Driver (1976)

指向結構體的指標

和其他任何型別一樣,結構體也可以被其自己型別的指標指向:

1
2
3
4
5
6
7
struct movies_t {
  string title;
  int year;
};

movies_t amovie;
movies_t * pmovie;

這裡 amovie 是一個 movies_t 結構型別的物件,而 pmovie 是一個指向 movies_t 結構型別物件的指標。因此,下面的程式碼也是有效的:

1
pmovie = &amovie;

指標 pmovie 的值將被賦為物件 amovie 的地址。

現在,讓我們看另一個混合了指標和結構體的例子,它將引出一個新的運算子:箭頭運算子(->)。

// pointers to structures
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

struct movies_t {
  string title;
  int year;
};

int main ()
{
  string mystr;

  movies_t amovie;
  movies_t * pmovie;
  pmovie = &amovie;

  cout << "Enter title: ";
  getline (cin, pmovie->title);
  cout << "Enter year: ";
  getline (cin, mystr);
  (stringstream) mystr >> pmovie->year;

  cout << "\nYou have entered:\n";
  cout << pmovie->title;
  cout << " (" << pmovie->year << ")\n";

  return 0;
}
Enter title: Invasion of the body snatchers
Enter year: 1978
 
You have entered:
Invasion of the body snatchers (1978)

箭頭運算子(->)是一個解引用運算子,專門用於指向含有成員的物件的指標。這個運算子用於直接從物件的地址訪問其成員。例如,在上面的例子中:

1
pmovie->

在所有方面,都等同於:

1
(*pmovie).

pmovie->title(*pmovie).title 這兩個表示式都是有效的,都訪問了由名為 pmovie 的指標所指向的資料結構的成員 title。這與下面的表示式完全不同:

1
*pmovie.

它實際上等同於:

1
*(pmovie.title)

這將訪問結構物件 pmovie 中一個假設存在的名為 title 的指標成員所指向的值(但實際情況並非如此,因為 title 不是指標型別)。下表總結了指標運算子和結構成員運算子的可能組合:

表示式求值內容等價於
a.b物件 a 的成員 b
a->ba 指向的物件的成員 b(*a).b
*a.b物件 a 的成員 b 所指向的值*(a.b)

巢狀結構體

結構體也可以巢狀,即一個結構體的元素本身是另一個結構體:

1
2
3
4
5
6
7
8
9
10
11
12
struct movies_t {
  string title;
  int year;
};

struct friends_t {
  string name;
  string email;
  movies_t favorite_movie;
} charlie, maria;

friends_t * pfriends = &charlie;

在上述宣告之後,以下所有表示式都將是有效的:

1
2
3
4
charlie.name
maria.favorite_movie.title
charlie.favorite_movie.year
pfriends->favorite_movie.

(順便說一下,最後兩個表示式引用的是同一個成員)。
Index
目錄