1.内置类型:
(1).基本类型:整形(int,char,Boolean),浮点型(double,float),void
(2)复合类型:数组,结构体,指针,引用,枚举,共同体
2.自定义类型:
(1)用户自定义类
(2)标准库中定义的类型(STL标准模板库)
( 数组只能储存同种类型的数据。)
结构体:由一系列具有相同类型或不同类型的数据构成的数据集合。
结构体的声明:
struct 类型名{
数据类型1 成员名1;
数据类型2 成员名2;
……
}
定义结构体变量的格式:
struct 结构体类型名 变量名列表;
将结构体类型声明与变量定义合在一起:
struct 类型名{
数据类型1 成员名1;
数据类型2 成员名2;
……
}变量名;
3.结构体的使用:
结构体变量具有以下特点:
(1)可以对结构体变量的整体进行部分操作。eg:swap(a,b)(注意:两个变量同种类型。)
(2)对结构体变量的成员进行操作。
格式:结构体变量名.成员名
(3)结构体变量的初始化与数组类似。
例题:
(1).学生信息的输入与输出。
#include //万能头文件,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
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
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
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<s.height)
return true;//次序保持不变
else if(height==s.height)return num>n;
for(int i=1;i<=n;i++)
a[i].input();
sort(a+1;a+n+1);//快速排序
a[1].output();//最高的同学
return 0;
}
1.string的简单使用:
string表示可变长度的字符序列。使用string类要包含头文件(注意区别
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 = π //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的引用
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*,不指向常量
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)