簡單說結構體,就是一個可以包含不同類型的資料結構,他是一種可以自己定義的資料類型。他和數組的主要差別——結構體可以在結構中聲明不同的資料類型,相同的結構體可以互相指派,而數組是做不到的,因為數組是單一資料類型的資料集合,它本身不是資料類型(而結構體是),數組名稱是常量指針,是以不可以做為左值進行運算,是以數組之間就不能通過數組名稱互相複制了,即使資料類型和數組大小完全相同。
簡單的結構體定義:
struct test
{
float a;
int b;
};
由于結構體本身就是自定義的資料類型,定義結構體變量的方法和定義普通變量的方法一樣——test pn1;
這樣就定義了一test結構體資料類型的結構體變量pn1,結構體成員的通路通過點操作符進行,pn1.a=10 就對結構體變量pn1的成員a進行了指派操作。
注意:結構體生命的時候本身不占用任何記憶體空間,隻有當你用你定義的結構體類型定義結構體變量的時候計算機才會配置設定記憶體
結構體,同樣是可以定義指針的,那麼結構體指針就叫做結構指針。
結構指針通過->符号來通路成員,下面我們就以上所說的看一個完整的例子:
#include <iostream>
#include <string>
using namespace std;
struct test//定義一個名為test的結構體
{
int a;//定義結構體成員a
int b;//定義結構體成員b
};
void main()
{
test pn1;//定義結構體變量pn1
test pn2;//定義結構體變量pn2
pn2.a=10;//通過成員操作符.給結構體變量pn2中的成員a指派
pn2.b=3;//通過成員操作符.給結構體變量pn2中的成員b指派
pn1=pn2;//把pn2中所有的成員值複制給具有相同結構的結構體變量pn1
cout<<pn1.a<<"|"<<pn1.b<<endl;
cout<<pn2.a<<"|"<<pn2.b<<endl;
test *point;//定義結構指針
point=&pn2;//指針指向結構體變量pn2的記憶體位址
cout<<pn2.a<<"|"<<pn2.b<<endl;
point->a=99;//通過結構指針修改結構體變量pn2成員a的值
cout<<pn2.a<<"|"<<pn2.b<<endl;
cout<<point->a<<"|"<<point->b<<endl;
cin.get();
}
總之,結構體可以描述數組不能夠清晰描述的結構,它具有數組所不具備的一些功能特性。
下面我們來看一下,結構體變量是如何作為函數參數進行傳遞的。
struct test
{
char name[10];
float socre;
};
void print_score(test pn)//以結構變量進行傳遞
{
cout<<pn.name<<"|"<<pn.socre<<endl;
}
void print_score(test *pn)//一結構指針作為形參
{
cout<<pn->name<<"|"<<pn->socre<<endl;
}
void main()
{
test a[2]={{"marry",88.5},{"jarck",98.5}};
int num = sizeof(a)/sizeof(test);
for(int i=0;i<num;i++)
{
print_score(a[i]);
}
for(int i=0;i<num;i++)
{
print_score(&a[i]);
}
cin.get();
}
void print_score(test *pn)的效率是要高過void print_score(test pn)的,因為直接記憶體操作避免了棧空間開辟結構變量空間需求,節省記憶體。
結構體引用傳遞
利用引用傳遞的好處很多,它的效率和指針相差無幾,但引用的操作方式和值傳遞幾乎一樣,種種優勢都說明善用引用可以做到程式的易讀和易操作,它的優勢尤其在結構和大的時候,避免傳遞結構變量很大的值,節省記憶體,提高效率
#include <iostream>
#include <string>
using namespace std;
struct test
{
char name[10];
float socre;
};
void print_score(test &pn)//以結構變量進行傳遞
{
cout<<pn.name<<"|"<<pn.socre<<endl;
}
void main()
{
test a[2]={{"marry",88.5},{"jarck",98.5}};
int num = sizeof(a)/sizeof(test);
for(int i=0;i<num;i++)
{
print_score(a[i]);
}
cin.get();
}
上面我們說明了易用引用對結構體進行操作的優勢,下面我們重點對比兩個例程,進一部分析關于效率的問題。
//-------------------------------------例程1---------------------------------
#include <iostream>
#include <string>
using namespace std;
struct test
{
char name[10];
float socre;
};
void print_score(test &pn)
{
cout<<pn.name<<"|"<<pn.socre<<endl;
}
test get_score()
{
test pn;
cin>>pn.name>>pn.socre;
return pn;
}
void main()
{
test a[2];
int num = sizeof(a)/sizeof(test);
for(int i=0;i<num;i++)
{
a[i]=get_score();
}
cin.get();
for(int i=0;i<num;i++)
{
print_score(a[i]);
}
cin.get();
}
//-------------------------------------例程2---------------------------------
#include <iostream>
#include <string>
using namespace std;
struct test
{
char name[10];
float socre;
};
void print_score(test &pn)
{
cout<<pn.name<<"|"<<pn.socre<<endl;
}
void get_score(test &pn)
{
cin>>pn.name>>pn.socre;
}
void main()
{
test a[2];
int num = sizeof(a)/sizeof(test);
for(int i=0;i<num;i++)
{
get_score(a[i]);
}
cin.get();
for(int i=0;i<num;i++)
{
print_score(a[i]);
}
cin.get();
}
例程2的效率要遠高過例程1的原因主要有以下兩處:
第一:
例程1中的
test get_score()
{
test pn;
cin>>pn.name>>pn.socre;
return pn;
}
調用的時候在内部要在棧空間開辟一個名為pn的結構體變量,程式pn的時候又再次在棧記憶體空間内自動生成了一個臨時結構體變量temp,在前面的教程中我們已經說過,它是一個copy,而例程2中的:
void get_score(test &pn)
{
cin>>pn.name>>pn.socre;
}
卻沒有這一過程,不開辟任何新的記憶體空間,也沒有任何臨時變量的生成。
第二:
例程1在mian()中,必須對傳回的結構體變量進行一次結構體變量與結構體變量直接的互相指派操作。
for(int i=0;i<num;i++)
{
a[i]=get_score();
}
而例程2中由于是通過記憶體位址直接操作,是以完全沒有這一過程,提高了效率。
for(int i=0;i<num;i++)
{
get_score(a[i]);
}
函數也是可以傳回結構體引用的,例子如下:
#include <iostream>
#include <string>
using namespace std;
struct test
{
char name[10];
float socre;
};
test a;
test &get_score(test &pn)
{
cin>>pn.name>>pn.socre;
return pn;
}
void print_score(test &pn)
{
cout<<pn.name<<"|"<<pn.socre<<endl;
}
void main()
{
test &sp=get_score(a);
cin.get();
cout<<sp.name<<"|"<<sp.socre;
cin.get();
}
調用get_score(a);結束并傳回的時候,函數内部沒有臨時變量的産生,傳回直接把全局結構變量a的記憶體位址賦予結構引用sp
最後提一下指針的引用
定義指針的引用方法如下:
void main()
{
int a=0;
int b=10;
int *p1=&a;
int *p2=&b;
int *&pn=p1;
cout <<pn<<"|"<<*pn<<endl;
pn=p2;
cout <<pn<<"|"<<*pn<<endl;
cin.get();
}
pn就是一個指向指針的引用,它也可以看做是指針别名,總之使用引用要特别注意它的特性,它的操作是和普通指針一樣的,在函數中對全局指針的引用操作要十分小心,避免破壞全局指針!
在這裡對C++的引用做一下說明:
引用是C++中的概念,初學者容易把引用和指針混淆一起。一下程式中,n是m的一個引用(reference),m是被引用物(referent)。
int m;
int &n = m;
n相當于m的别名(綽号),對n的任何操作就是對m的操作。例如有人名叫王小毛,他的綽号是“三毛”。說“三毛”怎麼怎麼的,其實 就是對王小毛說三道四。是以n既不是m的拷貝,也不是指向m的指針,其實n就是m它自己。
引用的一些規則如下:
(1)引用被建立的同時必須被初始化(指針則可以在任何時候被初始化,最好也在建立時被初始化。)。
(2)不能有NULL引用,引用必須與合法的存儲單元關聯(指針則可以是NULL)。
(3)一旦引用被初始化,就不能改變引用的關系(指針則可以随時改變所指的對象)。
例如下例的當改變k時,I的值也随之改變,這就證明不能改變其關系。
以下示例程式中,k被初始化為i的引用。語句k = j并不能将k修改成為j的引用,隻是把k的值改變成為6。由于k是i的引用,是以i的值也變成了6。
int i = 5;
int j = 6;
int &k = i;
k = j; // k和i的值都變成了6;
上面的程式看起來象在玩文字遊戲,沒有展現出引用的價值。引用的主要功能是傳遞函數的參數和傳回值。C++語言中,函數的參數和傳回值的傳遞方式有三種:值傳遞、指針傳遞和引用傳遞。
以下是“值傳遞”的示例程式。由于Func1函數體内的x是外部變量n的一份拷貝,改變x的值不會影響n, 是以n的值仍然是0。
void Func1(int x)
{
x = x + 10;
}
…
int n = 0;
Func1(n);
cout << “n = ” << n << endl; // n = 0
以下是“指針傳遞”的示例程式。由于Func2函數體内的x是指向外部變量n的指針,改變該指針的内容将導緻n的值改變,是以n的值成為10。
void Func2(int *x)
{
(* x) = (* x) + 10;
}
…
int n = 0;
Func2(&n);
cout << “n = ” << n << endl; // n = 10
以下是“引用傳遞”的示例程式。由于Func3函數體内的x是外部變量n的引用,x和n是同一個東西,改變x等于改變n,是以n的值成為10。
void Func3(int &x)
{
x = x + 10;
}
…
int n = 0;
Func3(n);
cout << “n = ” << n << endl; // n = 10
對比上述三個示例程式,會發現“引用傳遞”的性質象“指針傳遞”,而書寫方式象“值傳遞”。實際上“引用”可以做的任何事情“指針”也都能夠做,為什麼還要“引用”這東西?
答案是“用适當的工具做恰如其分的工作”。
指針能夠毫無限制地操作記憶體中的如何東西,盡管指針功能強大,但是非常危險。就象一把刀,它可以用來砍樹、裁紙、修指甲、理發等等,誰敢這樣用?
如果的确隻需要借用一下某個對象的“别名”,那麼就用“引用”,而不要用“指針”,以免發生意外。比如說,某人需要一份證明,本來在檔案上蓋上公章的印子就行了,如果把取公章的鑰匙交給他,那麼他就獲得了不該有的權利。