C++ 高级数据类型
本文先介绍C++ 高级数据类型:结构体、联合(公用)、枚举。最后介绍typedef。
结构体(structure)类型
C++里面使用结构体类型能够将不同的数据类型组合成为一个整体——结构体类型。
使用结构体数据类型的主要目的是:将一些零散的信息给整合在一块,方便管理。
例如一个学生实体,有姓名、身高、年龄、学号……等属性,如果使用基本数据类型,那么得定义很多歌变量,并且这些变量之间“表面上”看起来是没有联系的。
结构体类型的声明
必须首先声明结构体,然后才能够定义该类型的变量实例。结构体就是由不同数据类型也可以是相同数据类型的若干数据组成的结合体,结构体声明的关键字为struct。
有四种声明形式
☆第一种语法表示
struct 结构体名称{
数据类型 成员名1;
数据类型 成员名2;
…
数据类型 成员名n;
};
其中最后那个分号也不要忘记。
结构体类型例1:
#include<iostream>
#include <cstring> //因为后面用到C风格字符串操作如strcpy函数
using namespace std;
struct Student{
int sNo;
char name[10];
};
int main(){
struct Student stu;
//stu.name="zhangsan";//对stu.name这种赋值方式是不对的
strcpy(stu.name, "zhangsan"); //对stu.name可如此赋值
stu.sNo=18;
cout<<stu.name<<endl;
cout<<stu.sNo<<endl;
return 0;
}
运行之,参见下图:
☆第二种语法表示
typedef struct 结构体名称{
数据类型 成员名1;
数据类型 成员名2;
…
数据类型 成员名n;
}结构体名称别名;
其中typedef关键字的作用就是声明数据类型的别名,本文后面还将介绍typedef。
结构体类型例2:
#include<iostream>
#include <cstring> //因为后面用到C风格字符串操作如strcpy函数
using namespace std;
typedef struct Student{
int sNo;
char name[10];
} stud;
int main(){
struct Student stu;//方式一
stud stu1;//方式二,以结构体别名声明一个结构变量stu1,此处可以省略关键字struct
//stu.name="zhangsan"; //这种赋值方式是不对的
strcpy(stu.name, "zhangsan"); //对stu.name可如此赋值
stu.sNo=18;
stu1.sNo=19;
cout<<stu.name<<endl;
cout<<stu.sNo<<endl;
cout<<stu1.sNo<<endl;
return 0;
}
运行之,参见下图:
☆第三种方式
struct 结构体名称{
数据类型 成员名1;
数据类型 成员名2;
…
数据类型 成员名n;
}结构体变量;
结构体类型例3:
#include<iostream>
#include <cstring> //因为后面用到C风格字符串操作如strcpy函数
using namespace std;
struct Student{
int sNo;
char name[10];
} stu;//这里相当于直接声明并定义了一个结构体变量stu
int main(){
//struct Student stu;//错误,这里就不可以再这样声明变量
//stud stu1;///错误,
//stu.name="zhangsan";//这种赋值方式是不对的
strcpy(stu.name, "zhangsan"); //对stu.name可如此赋值
stu.sNo=18;
cout<<stu.name<<endl;
cout<<stu.sNo<<endl;
return 0;
}
运行之,参见下图:
☆第四种方式
struct {
数据类型 成员名1;
数据类型 成员名2;
…
数据类型 成员名n;
}结构体变量名;
这是匿名(anonymous)定义方式,这种方式可能提示Warning(警告),建议不使用。
结构体类型例4:
#include<iostream>
using namespace std;
struct {
int sNo;
char name[10];
} stu1,stu2;//这里相当于直接声明并定义了2个结构体变量stu1,stu2
int main(){
strcpy(stu1.name, "zhangsan");
stu1.sNo=18;
stu2.sNo=8;
cout<<stu1.name<<endl;
cout<<stu1.sNo<<endl;
cout<<stu2.sNo<<endl;
}
可以先声明了结构体类型以后再定义结构体变量,也可以把它们放在一起。
结构体成员的使用形式是:结构体变量名.成员名。
在定义结构体变量时可以直接赋初值,例:
#include<iostream>
#include <cstring> //因为后面用到C风格字符串操作如strcpy函数
using namespace std;
struct student{
int sNo;
char name[10];
} stu={18,"zhangsan"}; // 声明结构体类型的同时,定义结构体类型的变量,并赋初值。
int main(){
cout<<stu.sNo<<endl;
cout<<stu.name <<endl;
return 0;
}
运行之,参见下图:
结构体一经声明,后面的程序中就可以像基本数据类型那样,使用结构体类型名类定义结构体类型的变量了。
结构体中的数据成员亦可以是另外一个结构体变量:
struct Date{
int year;
int month;
int day;
};
struct Book {
int no;
char name[40];
char publisher[20];
float price;
struct Date pub_date; // Date结构体
};
结构体和指针变量
一个指针变量当用来指向一个结构变量时,称之为结构指针变量。结构指针变量中的值是所指向的结构变量的首地址。通过结构指针即可访问该结构变量,这与数组指针和函数指针的情况是相同的。
结构指针变量说明的一般形式为:
struct 结构名 *结构指针变量名
例如,在前面的例题中定义了stu这个结构,如要说明一个指向stu的指针变量pstu,可写为:
struct stu *pstu;
当然也可在定义stu结构时同时说明pstu。与前面讨论的各类指针变量相同,结构指针变量也必须要先赋值后才能使用。
赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。如果boy是被说明为stu类型的结构变量,则:
pstu=&boy
是正确的,而:
pstu=&stu
是错误的。
结构名和结构变量是两个不同的概念,不能混淆。结构名只能表示一个结构形式,编译系统并不对它分配内存空间。只有当某变量被说明为这种类型的结构时,才对该变量分配存储空间。因此上面&stu这种写法是错误的,不可能去取一个结构名的首地址。有了结构指针变量,就能更方便地访问结构变量的各个成员。
其访问的一般形式为:
(*结构指针变量).成员名
其中,(*结构指针变量)两侧的括号不可少。
或:
结构指针变量->成员名
其中,->是间接成员运算符(也称为箭头运算符)
例如:
(*pstu).num
或者:
pstu->num
应该注意(*pstu)两侧的括号不可少,因为成员符“.”的优先级高于“*”。如去掉括号写作*pstu.num则等效于*(pstu.num),这样,意义就完全不对了。
下面通过例子来说明结构指针变量的具体说明和使用方法。
例
#include <iostream>
using namespace std;
struct stu
{
int num;
//char *name;
char name[20];
char sex;
float score;
} boy1={102,"Li Ming",'M',78.5},*pstu;
main()
{
pstu=&boy1;
cout<<"num=" << boy1.num <<" Name=" << boy1.name<<endl;
cout<<"sex=" << boy1.sex <<" Score=" << boy1.score<<endl;
cout<<"num=" << (*pstu).num <<" Name=" << (*pstu).name<<endl;
cout<<"sex=" << (*pstu).sex <<" Score=" << (*pstu).score<<endl;
cout<<"num=" << pstu->num <<" Name=" << pstu->name<<endl;
cout<<"sex=" << pstu->sex <<" Score=" << pstu->score<<endl;
return 0;
}
运行之,参见下图:
本例程序定义了一个结构stu,定义了stu类型结构变量boy1并作了初始化赋值,还定义了一个指向stu类型结构的指针变量pstu。在main函数中,pstu被赋予boy1的地址,因此pstu指向boy1。然后在printf语句内用三种形式输出boy1的各个成员值。从运行结果可以看出:
结构变量.成员名
(*结构指针变量).成员名
结构指针变量->成员名
这三种用于表示结构成员的形式是完全等效的。
联合(Union)
联合是类型不同,数目固定的若干个变量的集合。组成联合的若干个成员是共用一个内存地址。
联合(Union) 使得同一段内存可以被按照不同的数据类型来访问,数据实际是存储在同一个位置的。它的声明和使用看起来与结构(structure)十分相似,但实际功能是完全不同的:
union model_name {
type1 element1;
type2 element2;
type3 element3;
…
} object_name;
union 中的所有被声明的元素占据同一段内存空间,其大小取声明中最长的元素的大小。
union date
{
char c;
int i;
double d;
} ;
union date d1,*p;
联合成员的表示方法:
一般联合变量成员:<联合变量名>.<联合成员名> d1.c
指向联合变量指针成员:<指向联合变量指针名> -> <联合成员名> p->c
联合变量的赋值:
一般联合变量赋值: d1.c=‘m';
指向联合变量指针赋值: p ->c='m';
注意:
联合成员共址的特征使得只能对联合变量成员赋值,不能对联合变量赋值。
又如:
union mytypes_t {
char c;
int i;
float f;
} mytypes;
定义了3个元素:
mytypes.c
mytypes.i
mytypes.f
每一个是一种不同的数据类型。既然它们都指向同一段内存空间,改变其中一个元素的值,将会影响所有其他元素的值。
union 的用途之一是将一种较长的基本类型与由其它比较小的数据类型组成的结构(structure)或数组(array)联合使用,例如:
union mix_t{
long l;
struct {
short hi;
short lo;
} s;
char c[4];
} mix;
以上例子中定义了3个名称:mix.l, mix.s 和 mix.c,我们可以通过这3个名字来访问同一段4 bytes长的内存空间。至于使用哪一个名字来访问,取决于我们想使用什么数据类型,是long, short 还是 char 。
匿名联合(Anonymous union)
在 C++ 我们可以选择使联合(union)匿名。如果我们将一个union包括在一个结构(structure)的定义中,并且不赋予它object名称 (就是跟在花括号{}后面的名字),这个union就是匿名的。这种情况下我们可以直接使用union中元素的名字来访问该元素,而不需要再在前面加 union对象的名称。在下面的例子中,我们可以看到这两种表达方式在使用上的区别:
union |
anonymous union |
struct { char title[50]; char author[50]; union { float dollars; int yens; } price; } book; |
struct { char title[50]; char author[50]; union { float dollars; int yens; }; } book; |
以上两种定义的唯一区别在于左边的定义中我们给了union一个名字price,而在右边的定义中我们没给。在使用时的区别是当我们想访问一个对象(object)的元素dollars 和yens 时,在前一种定义的情况下,需要使用:
book.price.dollars
book.price.yens
而在后面一种定义下,我们直接使用:
book.dollars
book.yens
再一次提醒,因为这是一个联合(union),域dollars 和yens 占据的是同一块内存空间,所以它们不能被用来存储两个不同的值。也就是你可以使用一个dollars 或yens的价格,但不能同时使用两者。
枚举(enumeration)
如果一个变量你需要几种可能存在的值,那么就可以被定义成为枚举类型。之所以叫枚举就是说将变量或者叫对象可能存在的情况也可以说是可能的值一一例举出来。
枚举(Enumerations)可以用来生成一些任意类型的数据,不只限于数字类型或字符类型,甚至常量true 和false。它的定义形式如下:
enum model_name {
value1,
value2,
value3,
…
} object_name;
例如,我们可以定义一种新的变量类型叫做color_t 来存储不同的颜色:
enum colors_t {black, blue, green, cyan, red, purple, yellow, white};
注意在这个定义里我们没有使用任何基本数据类型。换句话说,我们创造了一种的新的数据类型,而它并没有基于任何已存在的数据类型:类型color_t,花括号{}中包括了它的所有的可能取值。例如,在定义了colors_t 列举类型后,我们可以使用以下表达式 :
colors_t mycolor;
mycolor = blue;
if (mycolor == green) mycolor = red;
实际上,我们的枚举数据类型在编译时是被编译为整型数值的,而它的数值列表可以是任何指定的整型常量 。如果没有指定常量,枚举中第一个列出的可能值为0 ,后面的每一个值为前面一个值加1。因此,在我们前面定义的数据类型colors_t 中,black 相当于0, blue 相当于 1, green 相当于2 ,后面依此类推。
如果我们在定义枚举数据类型的时候明确指定某些可能值(例如第一个)的等价整数值,后面的数值将会在此基础上增加,例如:
enum months_t { january=1, february, march, april,
may, june, july, august,
september, october, november, december} y2k;
在这个例子中,枚举类型months_t的变量y2k 可以是12种可能取值中的任何一个,从january 到 december ,它们相当于数值1 到 12,而不是0 到 11 ,因为我们已经指定 january 等于1。
typedef
typedef用于给已有的类型一个新的名字,即C++ 允许我们在现有数据类型的基础上定义我们自己的数据类型。我们将用关键字typedef来实现这种定义,它的形式(格式)是:
typedef existing_type new_type_name;
这里 existing_type 是C++ 基本数据类型或其它已经被定义了的数据类型,new_type_name 是我们将要定义的新数据类型的名称。例如:
typedef char C;
typedef unsigned int WORD;
typedef char * string_t;
typedef char field [50];
在上面的例子中,我们定义了四种新的数据类型: C, WORD, string_t 和 field ,它们分别代替 char, unsigned int, char* 和 char[50] 。这样,我们就可以安全的使用以下代码:
C achar, anotherchar, *ptchar1;
WORD myword;
string_t ptchar2;
field name;
如果在一个程序中我们反复使用一种数据类型,而在以后的版本中我们有可能改变该数据类型的情况下,typedef 就很有用了。或者如果一种数据类型的名称太长,你想用一个比较短的名字来代替以方便编码,也可以是用typedef实现。
为结构体定义别名,可见前面结构体部分,不在多说。