复合数据类型

复合数据类型

一、数据类型可分为两大类:

1.内置类型:
(1).基本类型:整形(int,char,Boolean),浮点型(double,float),void
(2)复合类型:数组,结构体,指针,引用,枚举,共同体
2.自定义类型:
(1)用户自定义类
(2)标准库中定义的类型(STL标准模板库)

二、结构体

( 数组只能储存同种类型的数据。)

  1. 结构体:由一系列具有相同类型或不同类型的数据构成的数据集合。

  2. 结构体的声明:
    struct 类型名{
    数据类型1 成员名1;
    数据类型2 成员名2;
    ……
    }
    定义结构体变量的格式:
    struct 结构体类型名 变量名列表;

       将结构体类型声明与变量定义合在一起:
       struct 类型名{
       		  数据类型1  成员名1;
       		  数据类型2  成员名2;
       		  ……
       		}变量名;
    

3.结构体的使用:
结构体变量具有以下特点:
(1)可以对结构体变量的整体进行部分操作。eg:swap(a,b)(注意:两个变量同种类型。)
(2)对结构体变量的成员进行操作。
格式:结构体变量名.成员名
(3)结构体变量的初始化与数组类似。
例题:
(1).学生信息的输入与输出。

#include<bits/stdc++.h>//万能头文件,gcc4.9以上的编译器均可以使用。
using namespace std;
struct student{
     string name;
     char sex;
     int age;
     double weight;
};//注意分号
int main(){
     student stu;//定义新的变量stu
     cin >> stu.name >> stu.sex >> stu.age >> stu.weight;
     cout << stu.name <<  “ “ << stu.sex <<  “ “ << stu.age <<  “ “ ;//输入输出依次逐项
     cout << fixed << setprecision(1) << stu.weight << endl;
     return 0;
}

(2)年龄排序(从小到大,保证无同年同月出生的学生。)

#include<bits/stdc++.h>
using namespace std;
struct stu{
    string name;
    string sex;
    int year,month;//同种类型可一起定义
};//定义结构体类型保存一个学生的信息
const int MAXN = 110;//定义常量,比数据规模较大
stu a[MAXN];//定义结构体数组保存所有学生的信息。数组元素个数要确定。
int main(){
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++)
          cin >> a[i].name >> a[i].sex >> a[i].year >> a[i].month;//注意要按成员输入
    for(int i = 1; i <= n; i++)
          for(int j = i+1; j <= n; j++)
                if(a[i].year < a[j].year || a[i].year == a[j].year && a[i].month < a[j].month)
                       swap(a[i],a[j]);//通过年份与月份对结构体数组进行选择排序。此处为结构体变量的整体操作(赋值)。
    for(int i = 1; i <= n; i++){
          cout<< a[i].name <<  ” ” << a[i].sex <<  ” ” ;
          cout<< a[i].year <<  ” ” << a[i].month << endl;
    }
    return 0;
}

ps:排序可以用sort函数。sort函数可以针对所有的数据类型。

4.结构体的扩展
(1)运算符重载(成员函数):常用于解决结构体或自定义数据类型的加法,减法等特殊含义的运算。
格式:
类型名 operator 运算符(const 类型名 变量)const{
……
}

例题:
【问题描述】
为了了解学生的课后作业负担情况,需要统计学生连续若干天完成作业所需的总时间。现在,输入某位学生 n 天完成作业的时间,格式为时、分、秒,最后输出这位学生 n 天完成作业的总时间(秒)。
【输入格式】
第 1 行一个正整数 n,表示有 n 天;
第 2~ 第 n+1 行,每行 3 个整数,分别代表时、分、秒。
【输出格式】
一行信息,表示这个学生完成作业的总时间,具体格式见输出样例。
【输入样例】
3
1 20 30
1 20 45
1 19 30
【输出样例】
4hour 0minute 45second


```cpp
#include<bits/stdc++.h>
using namespace std;
struct worktime{// 声明一个结构体类型 worktime 记录学生完成作业的时间
     int hr,minut,sec; // hr,minut,sec 分别代表时分秒
     worktime operator +(const worktime x)const{ // 对 + 进行重新定义
         worktime tmp;
                  tmp.sec = (sec + x.sec) % 60;//当前结构体的变量与形参的变量的和
                  tmp.minut = (minut + x.minut + (sec + x.sec) / 60) % 60;
                  tmp.hr = hr + x.hr + (minut + x.minut + (sec + x.sec) / 60) / 60;
                  return tmp;
          }
};
int main(){
    worktime stu,sum;
    int n;
    cin >> n;
    sum.hr = sum.minut = sum.sec = 0;
    for(int i = 1; i <= n; i++){
          cin >> stu.hr >> stu.minut >> stu.sec;
          sum = sum + stu;// 两个结构体 sum 和 stu 通过重载 + 运算符进行加法运算
    }
    cout << sum.hr <<  ” hour ” << sum.minut <<  ” minute ” << sum.sec <<  ” second ” ;
    return 0;
}

(2)成员函数
struct 结构名 {
数据成员
成员函数
};
例题:身高问题
输出身高最高的学生。

#include<bits/stdc++.h>
using namespace std;
struct stu{
        string name;
        int heigh;
        int num;
        void input(){
                 cin >> name >> heigh >> num;
        }//成员函数
        void output(){
                 cout << name <<  “ “ << heigh <<  “ “ << num << endl;
        }
 };
 stu a[110];
 int main(){
    int n;
    stu maxn;
    maxn.heigh = maxn.num = 0;
    cin >> n;
    for(int i = 1; i <= n; i++){
                    a[i].input();//调用成员函数
                    if(a[i].heigh > maxn.heigh) maxn = a[i];//整体赋值
                    else if(a[i].heigh == maxn.heigh && a[i].num < maxn.num) maxn = a[i];
     }
     maxn.output();
     return 0;
}

重载小于号(重新定义运算符)。

struct stu{
string name;
int height;
int num;
void input(){
	cin>>name>>height>>num;
}
void input(){
	cout<<name<<" "<<height<<" "<<num<<endl;
}
bool operate <(const stu s)const{//两个操作数,一个形参(用const修饰),一个默认的结构体本身。
	if(height>s.height)
		return true;//次序保持不变
	else if(height==s.height)return num<s.num;
		else return false;//交换次序
}
};
stu a[110];//全局变量
int main()
{
	int n;
	maxn.height=maxn.num=0;
	cin>>n;
	for(int i=1;i<=n;i++)
		a[i].input();
	sort(a+1;a+n+1);//快速排序
	a[1].output();//最高的同学
	return 0;
}


三、 标准库string类***

1.string的简单使用:
string表示可变长度的字符序列。使用string类要包含头文件(注意区别<string.h>,这是针对string数组,c语言头文件)。

string s1, s2;			//创建两个空字符串对象
string s3 = "Hello, World!";	//创建s3,并初始化
string s4("I am ");
s2 = "Today";				//赋值
s1 = s3 + " " + s4;			//字符串连接
s1 += " 5 ";				//末尾追加连接
s4(n,'c');//把s4初始化为由连续n个c字符组成的串
cout << s1 + s2 + "!" <<endl;	//输出字符串内容
cout <<"Length of s1 is :" << s1.size() << endl;
//s1的长度,包含的元素个数
for (size_t i = 0; i < s1.size(); ++i) 
cout << s1[i] <<" ";			
}//逐个输出

2.读写string对象
使用标准库中iostream可以读写string对象。
键盘输入时,遇到空格或回车结束。
(1)可以用循环读取未知数量的string对象

//读取输入流中的单词,直到文件结束
string word;
while(cin >> word)//有效输入返回true
  cout << word << endl;	

(2)getline()函数。得到一段完整的文本,遇到回车结束。
注意:
a.两个参数:输入流对象和存放读入字符串的string对象
b.返回参数输入流对象

//每次读取一行文本,直到文件结束
string line;
while(getline(cin, line))
  cout << line << endl;	

(3)判断string对象是否为空。
empty()函数判断string对象是非为空,返回一个布尔值。

//每次读取一行文本,输出非空的行
string line;
while(getline(cin, line))			
  if(!line.empty())		//line不为空,输出
    cout << line << endl;	

(4) 比较string对象。
相等:程度相同且所包含字符也完全相同。
依照字典顺序比较。
(5)string对象的赋值和连接
运算符“+”连接。
"+"运算符要求至少有一个运算对象是string。
复合赋值运算符“+=”则将右操作数的内容追加到左操作数的后面。

string s1 = "hello, ", s2 = "world! " ;
string s3 = s1 + s2;
s1 += s2;
string s4 = s1 + "\n";			//正确
string s5 = "hello" + "\n";		//错误
string s6 = s1 + " world" + "\n";	//正确
string s5 = "hello" + "," + s2;		//错误

(6)string对象和C风格字符串
c语言中可以用c_str()操作可以把字符串转化成字符数组。

string s1 = "If you really want it.";
int x = strlen(s1);			//Error
x = strlen(s1.c_str());			//OK

四、指针与引用

1.内存地址与间接访问
(1)内存地址
程序运行时,代码和数据都被存储在内存中。
内存是有序的字节序列,每个字节都有唯一地址。
通过地址确定字节的位置,用以储存和获取数据。
(2)直接与间接访问
通过变量名直接访问。
使用变量的内存地址找到存放数据的单元,间接访问其中的内容。
通过指针间接访问。
2. 指针:
指针为一个变量的地址;
指针变量为专门存放变量地址的变量。
指针变量存储目标对象在内存中的位置。

(1) 指针变量的定义:
类型 *指针变量;(类型是,所绑定目标变量的类型)


```cpp
int  *pi;
int* pi;	
char *pc1, *pc2;
char* pc1, pc2;	
char *pc1, pc2;	
char* pc1, *pc2;

目标变量的地址和指针变量的绑定:通过取地址符&

int ival = 120;
int  *pi = &ival;	
		//初始化,取出ival的地址赋值给pi; pi存放int变量ival的地址
		// 或者说pi指向ival
char ch = 'a', *pc = &ch;
		// pc指向字符型变量ch

(2)指针解引用运算符“ * ”
如果指针指向一个对象,则可以通过指针间接访该对象,使用指针解引用运算符“ * ”

int x = 100, y = 20;
int *pi = &x;
*pi = y;		
		// 取内容,间接操作pi指向的x,即x = y,不是pi指向y
		

(3)指向对象的指针
指针变量的数据类型:(4字节)32位无符号整数。
指向一个对象的指针与两个存储单元关联:
a,一个是指针自己的存储单元,里面存放着所指对象的地址
b,另外一个是指针指向的对象的存储单元,里面存放在该对象的值

int ival = 1024;
int *pi = &ival;
int **ppi = &pi;  //ppi是指向指针的指针,存放pi的地址

在这里插入图片描述

int main( )
{
	int ival=1024;
	int *pi=&ival;
	cout << " sizeof(pi):" << sizeof(pi) << endl; //指针在内存中所占大小,无符号整数4字节
	cout << " sizeof(ival):" << sizeof(ival) << endl; //ival在内存中所占大小
	cout << " &pi:" << &pi << endl; //指针在内存中的地址,指针变量的地址
	cout << " pi:" << pi << endl; //指针中存放的内容,即ival的地址
	cout << " &ival:" << &ival << endl; //ival的地址
	cout << " *pi:" << *pi << endl; //指针所指内存中存放的内容,即ival的值
	cout << " ival:" << ival << endl; //ival的值
}

(4)指针的类型,即指针指向的目标变量的类型
(5)指针的值:指针不能保存非地址值,也不能被赋值或初始化为不同类型的地址值(不产生二义性)。

int ival = 100;
int *pi = &ival; 		// pi 被初始化为ival的地址
int *pi2 = ival;      	// 编译错误,ival不是地址
double dval = 1.5;
pi = &dval;			// 编译错误
pi2 = 0;      		// 正确:pi2是空指针

(6)空指针:
指针值为0时是一个空指针,即不指向任何对象的指针
2种方法:0;预处理常量NULL

// 生成空指针的2种方法	
int *p1 = 0;      	
int *p2 = NULL; 

//不能写成下面的样子:
int zero = 0;
int *p4 = zero;//要有具体指向

注意:
1.定义指针时,应该对指针进行初始化。(要有具体指向)
2.指针的运算:
a,同类型的指针,相等(==)或不相等(!=);
b,指针可以进行加减整数值的算数运算;
c,自增、自减运算适用于指向数组元素的指针。

(7) 通用指针void*(无类型)。
(8)指针的用法:
a,典型用法
构建链式的数据结构,如链表和树;
管理程序运行时,动态分配的对象;
作为函数的参数。(数组)
b,存储空间的分配(静态,有名字【编译时】,动态,没有名字,指针间接操作【运行时】)

动态存储空间的管理:
方法:new,delete
1,new运算符:
在堆上动态分配空间,创建对象,并返回对象的地址
一般将new返回的地址保存在指针变量中,以便间接访问堆上的对象。
(1)new表达式的三种方式:
a,分配单个对象:new 类型 或 new 类型(初始值)

int* ip1 = new int;
			//在堆上分配一个int类型的对象,返回它的地址
*ip1 = 512;	
			//堆上分配的这个对象只能通过指针间接操作
int* ip2 = new int(100);
			//在堆上分配一个int对象,
			//初始化为100,实参,分配一个空间,返回其地址

b,分配多个连续存储的对象:new 类型[数组的大小]

int* ipa = new int[100];
//在堆上分配一个大小为100的int数组并返回数组的首地址,分配若干个空间

不能对数组进行显式的初始化。
数组的大小不一定是常量,是元素元素的个数,不是字节数。

2.delete运算符
堆上的空间使用后必须释放,否则会造成内存泄漏。(即动态内存空间使用后未回收)
new运算符分配的空间用delete运算符释放方式:
(1)释放单个对象:delete 指针;

int* ip = new int;
... //不再使用这个int对象时,释放内存
delete ip;	
//释放指针指向的int对象,将空间归还给动态存储区

(2)释放连续若干空间,数组:delete[ ] 指针;

int* pa = new int[100];	
... //不再使用这个数组时,释放内存
delete[] pa;	
//释放指针pa指向的数组,将空间归还给动态存储区

空悬指针:
执行delete运算后,指针ip指向的空间被释放,不能再使用ip指向的内存,但是ip这个指针变量自己的存储空间不受影响(指针变量本身存在,数据也存在,绑定关系解除)。
delete后的ip不是空指针,而是“空悬指针”,即指向不确定的单元
delete之后,继续通过ip间接使用这个单元是非法的,会引起不可预料的运行错误

int *ip=new int;
*ip=512;//等价于int *ip=new int(512);
delete ip;
*ip=100;//错误,不可以对空悬指针操作

引用

1.引用类型
引用又称为别名,对象的另一个名字;
通过引用可以间接地操纵对象;
在程序中,引用主要用作函数的参数。

2.引用的定义和初始化
引用由类型标识符和一个说明符(&)来定义
type& refVariable = leftValue;
引用必须被初始化,初始值是一个有内存地址的对象

int ival = 100;
int &refVal = ival;	
int &refVal2;			//错,别名要有针对(绑定)
int &refVal3 = &ival;		//错
int &refVal4 = 10;		//错,不能绑定常量

(1)引用的初始化:
一般在初始化变量时,初始值会被复制到新建的对象中(赋值)
定义引用时,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用(绑定)
一旦初始化完成,引用将和它的初始化对象一直绑定,无法重新绑定另一个对象,所以引用必须初始化。

int x = 100, y = 20;
int &r = x;	// r是x的引用
r = y;		// x = y;x=20

注意:
引用只能绑定到对象上(有内存地址),不能与字面值或某个表达式的计算结果绑定在一起。

引用vs指针
1.定义形式
指针:
定义形式:类型 *指针变量;(可多次绑定)

int x = 10, y = 20;
int *pi;	//未初始化
pi = &x;	//与x绑定
pi = &y;	//与y绑定

引用:
定义形式:类型 &引用名=初始值;(只在初始化时绑定)

int a = 10, b=20;
int &ri = a;	
ri = b; 

2,使用方式
指针通过取内容*间接访问
引用作为对象的别名,可以直接访问

int x, a, *pi;
pi = &x;
*pi = 30;		// x=30
int &ri = a;
ri = 40;		//a=40

3.指针有空指针,引用没有空引用
指针可以不指向任何对象,值为0;
引用必须指向一个对象,且一直指向该对象,引用值若为0,表示它指向的单元值为0.

int a, *pi;
pi = 0;		// pi是空指针,不指向任何对象
int &ri = a;
ri = 0;		//a=0

4.赋值
指针之间的相互赋值会改变指向关系
引用之间的相互赋值是它们指向的对象之间的赋值,引用关系本身并不改变

int x = 100, y = 20;
int *p1 = &x, *p2 = &y;
p1 = p2;		// p1=&y, p1和p2都指向y
int &r1 = x, &r2 = y;	
r1 = r2;		// x = y, r1仍是x的引用

const限定指针

1.指向const对象的指针
const type * cp; 或者 type const * cp;
cp是指向常量的指针,它所指向的内存中的内容不可以改变,即*cp的值不能改变

const int ival = 1024;
int *pi = &ival;		
	//错误:试图将一个const地址赋值给一个非const指针
const int ival = 1024;
const int *pi = &ival;			//OK
//或者这样写:
int const *pi = &ival;			//OK
*pi = 500;  
	//错误:因为*pi是一个const int,常指针

2.指向非const对象的const指针
type* const cp = initAddressValue;
cp是常量指针,初始化后值不能改变,指向固定的单元

int ival = 1024;
int* const pi = &ival;

3.指向const对象的const指针
const type* const cp = initAddressValue;
cp是常量指针,且指向常量。

const int ival = 5;
const int* const pi = &ival; 

C++允许将一个非const地址赋值给const指针

int ival =1024;
const int *pi = &ival;
ival = 500;
		//OK,ival没有被限定为const,可以改变
*pi = 500;  
		//错误: 不可以通过pi改变ival,因为pi是const int*,不指向常量

const限定引用

1.const引用可以绑定到const对象
不能用非const引用指向const对象

const int ival = 5;
const int &r1 = ival;		
		//正确:引用和所引用的对象都是const int
r1 = 10;				
		//错误:r1是const的引用,不能修改
int &r2 = ival;			
		//错误:不能用非const引用指向const对象

2.const引用可以绑定到非const对象
但是const引用不能用来修改它所绑定的对象

int ival = 10;			
const int &r1 = ival;
		//正确:允许将const引用绑定到非const对象上
ival = 20;	//正确
r1 = 20;	//错误:r1是const引用,不能修改

3.const限定引用的含义

int ival = 5;
const int &r1 = ival;		
		//正确:r1可以绑定ival,但不能通过r1修改ival
int &r2 = ival;
r1 = 0;					
		//错误:r1是const引用,不能修改
r2 = 0;					
		// r2并非常量,可以修改ival为0

数组与指针
使用数组时一般会转换为指针

int ia[5];//ia和&ia[0]都表示数组的首地址,相当于int *const ia;

可以使用指针对数组进行访问

int a[10];
//指针访问数组元素
for(int *p = a; p < a+10; p++)//p++指向下一个元素;a+10<第11个元素
	cout << *p;//取内容

数组元素和地址
一维数组元素在内存中按下标顺序依次存放
一维数组a[n]的元素a[i]在内存中地址是a+i。
a[i]=*(a+i)

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值