C++学习之路DAY3—复合类型

一、数组

数组(array)是一种数据格式,能存储多个同类型的值

short months[12];

1.组成部分

  • 存储在每个元素中的值的类型
  • 数组名
  • 数组中的元素数

【1】数组中的元素不能是变量
【2】此时,months的类型不是“数组”,而是“short数组”

2.初始化

int cards[4]={3,6,8,10};
int hand[4];
hand[4]={5,6,7,9};//不允许
  • 只有在定义数组时才能使用初始化,此后就不能使用了
  • 不能将一个数组赋给另一个数组
long totals[500]={5.0,2.5};
short things[]={1,5,3,8};
  • 如果只对数组的一部分进行初始化,则编译器把其他元素设置为0
  • 如果初始化数组时方括号([ ])为空,则编译器将计算元素个数
int earnings[4]{1,2,3,4};
int x[10]={};
  • 进行初始化时,可以不用等号
  • 可不再大括号内包含任何东西,这将把所有元素都设置为0

二、字符串

通常,C++处理字符串的方式有两种。第一种来自C语言,常被称为C风格字符串。另一种,基于string类库的方法

1.利用char数组存储字符

char dog[8]={'b','e','a','u','x',' ','i','i'};
char cat[8]={'f','a','t','e','s','s','a','\0'};
  • C风格字符串的特殊性质:以空字符\0结尾,其ASCII码为0
  • 上例中,两个数组都是char数组,但只有第二个是字符串
  • C++中有许多处理字符的函数,包括cout使用的那些函数,它们逐个地处理字符串中的字符,直到到达空字符为止

更好的方法:

使用一个引号括起的字符串即可

char bird[11]="Mr.Cheeps.";
char fish[]="Bubbles";
  • 用引号括起的字符串隐式地包括结尾的字符
  • 字符串常量(使用双引号)不能与字符常量(使用单引号)互换

注意区别:

char shirt_size='s';
char shirt_size="s";
  • 字符常量(如‘S’)是字符串编码的简写表示,在ASCII系统上,‘S’只是83的另一种写法,因此将83赋给shirt_size
  • “s”不是字符常量,它表示的是两个字符(字符S和\0)组成的字符串,实际上,上述代码中,“s”表示的是字符串所在的内存地址

2.在数组中使用字符

将字符串存储到数组中,通常有两种方法:
1.将数组初始化为字符串常量
2.将键盘或文件输入读到数组中

#include<iostream>
#include<cstring>
char name1[15];
char name2[15]="C++owboy";

cout<<"hello!"<<name2;
cout<<"what's your name?\n";
cin>>name1;
cout<<strlen(name1)<<" "<<sizeof(name1);
  • sizeof运算出整个数组的长度
  • strlen返回的是存储在数组中的字符串的长度,即只计算可见的字符,不把空字符计算在内
  • 若需要修改程序以使用不同的数组长度时,只需要在定义符号常量的地方修改即可

3.字符串输入

cin输入

利用cin(),每次只能读取一个单词:

char name[20];
char dessert[size];

cout<<"enter your name\n";
cin>>name;
cout<<"enter your favorite dessert\n";
cin>>dessert;
cout<<"I have some ""<<dessert;
cout<<"for you."<<name;
  • 若输入单词,如name,程序正常运行不会出错
  • 若输入多个单词以空格隔开,则会出现问题,因为cin使用空白(空格、制表符和换行符)来确定字符串的结束位置:如上述程序中,输入abc xyz时,系统会把abc作为第一个字符串,将它放到name数组中,结果输出name为abc xyz;xyz则被留在输入队列中,当cin在输入附列中搜索用户喜欢的甜点时,发现了xyz,因此cin读取xyz,并将其存放在dessert中

getline()输入

geiline读取一行输入,直到达到换行符

char name[20];
char dessert[20];

cout<<"enter your name\n";
cin.getline>>(name,20);
cout<<"enter your favorite dessert\n";
cin.getline>>(dessert,20);
cout<<"I have some ""<<dessert;
cout<<"for you."<<name;
  • 上述更改的代码的意义为:将姓名读入到一个包含20个元素的name数组中

get()输入

get()与getline()作用相似,但get不再读取并丢弃换行符,而是将其留在输入队列中

#include<iostream>
using namespace std;
int main()
{
	char name[20];
	char dessert[20];
	cin.get(name,20);
	cin.get(dessert,20); 
}
  • 由于第一次调用后,换行符留在输入队列中,因此第二次调用时看到的第一个字符便是换行符,此时get()认为已到达行尾,第二次无法输入
  • 在两次cin.get输入的代码中间加入cin.get()可解决这一问题

4.混合输入字符串和数字

#include<iostream>
using namespace std;
int main()
{
	cout<<"what year was your house built?\n";
	int year;
	cin>>year;
	cout<<"what is its streer address?\n";
	char address[80];
	cin.getline(address,80);
}
  • 此程序运行时,用户根本没有输入地址的机会
  • 当cin读取年份时,同之前说过的cin输入的情况一样,回车键的换行符留在输入队列中,后面的cin.getline看到换行符之后认为是一个空行,并将一个空字符串赋给address
  • 在cin后加上一行代码cin.get()解决此问题,或直接合并写成(cin>>year).get()

三、string类

string类使用起来比数组简单,同时提供了将字符串作为一种数据类型的表示方法

#include<string>
string str1;
string str2="abcdefg";
cin>>str1;
string str3;
str3=str1+str2;
cout<<str2[2]<<endl;
strcpy(str1,str2)//复制操作
strcat(str1,str2)//拼接操作
  • 使用string对象的方式与使用字符数组相同:可初始化、输入存储、显示、访问string对象
  • string对象和字符数组之间的主要区别是,可以将string对象声明为简单变量,而不是数组
  • 不能将一个数组赋给另一个数组,但可以将一个string对象赋给另一个string对象str1=str2
  • string类简化了字符串合并操作

四、结构

假设现在要存储有关篮球运动员的信息,那么就要记录姓名、工资、身高、体重等等,其中,这些数据又有数字又有文字,数组无法满足,因为数组虽然可以存储多个元素,但所有元素的类型必须相同。那么,C++中的结构可以满足要求,因为同一个结构可以存储多种类型的数据。

1.定义结构

struct student
{
  char name[10];
  int number;
  char sex;
};

2.创建变量

student s1;//可单独声明,也可以
student s2;//在反括号后,分号前定义
student s3;

3.访问成员:

s1.name;
s2.name;
s3.sex;

4.结构声明的位置:

struct student//外部声明,可以用在所有函数中
{
};
int main()
{
struct student//局部声明,只能用在这个函数中
{
};
}

5.结构初始化:

student s1={"张三"',12345678,"男"};

6.将string类作为成员

#include <iostream>
#include <string>
using namespace std
struct student
{
string name;
float verb;
};

7.其他属性

可以将结构作为参数传递给函数,也可以让函数返回一个结构,也可以使用赋值运算符(=)将结构赋值给另一个同类型的结构,这样结构中每个成员都被设置为另一个结构中相应成员的值(即使成员是数组)

#include <iostream>
struct inflactable
{
char name[20];
float volume;
double price;
};
int main()
{
using namespace std;
inflactable bouquet=
{
"sunflowers",
0.20;
12.49;
};
inflactable choice;
cout<<"bouquet:"<<bouquet.name<<"for $";
cout<<bouquet.price<<endl;
choice=bouquet;
cout<<"choice:"<<choice.name<<"for $";
cout<<choice.price<<endl;
return 0;

8.结构数组

inflatable结构包含一个数组(name),也可以创建元素为结构的数组,方法和创建基本类型数组完全相同

inflatable gifts[100];

1.这样,gifts将是一个inflatable数组,其中的每一个元素都是inflatable对象:

cout<<gifts[99].price<<endl;

2.结构数组的初始化

inflatable guest[2]=
{
   {"A",1,22},
   {"B",2,33}
};

五、共用体

共用体(union)是一种数据格式,它能存储不同类型的数据类型,但只能同时存储其中一种类型,即结构可以同时存储int、long和double,但共用体只能存储int、long或double。它的用途之一是,当数据项使用两种或多种格式(但不会同时使用)时,可以节省空间

union one
{
int int_val;
long long_val;
double double_val;
};

one pail;
pail.int_val=15;
pail.double_val=9.99

六、枚举

枚举这种创建符号常量的方式,可以代替const,它还可以允许定义新类型

enum A{red,orange,yellow,green,blue};
A band;

1.A为新类型的名称,被称为枚举
2.可以用枚举名来声明这种类型的变量
3.可以用赋值运算符来显式地设置枚举量的值:enum A{one=1,two=2,three=3};

七、指针和自由存储空间

1.常规变量的地址

变量应用地址运算符(&)获得它的位置:

#include<iostream>
int main()
{
using namespace std;
int donuts=6;
double cuos=4.5;
cout<<"donuts value="<<donuts;
cout<<"and donuts address="<<&donuts<<endl;

2.指针

指针:用于存储值的地址,将地址视为指定的量,将值视为派生量。指针名表示的是地址,*运算符被称为间接值,将其应用于指针,可以得到改地址处存储的值

int updates=6;
int *p_updates;
p_updates=&updates;

cout<<"values:updates="<<updates;
cout<<"*p_updates"<<*p_updates<<endl;//values:updates=6 *p_updates=6

cout<<"address:&updates="<<&updates;
cout<<"p_updates="<<p_updates<<endl;//address:&updates=0x0065fd48 p_updates=0x0065fd48

3.声明和初始化指针

int *p_updates;//声明

1.这表明*p_updates的类型为int,由于 运算符被用于指针,因此p_updates变量本身必须是指针。我们说p_updates指向int类型,p_updates的类型是指向int的指针(或int
2.总的来说,p_updates是指针(地址),*p_updates是int

在这里插入图片描述

4.注意事项

  1. 合并或分开写
int *updates;
updates=&p_updates;

int *updates=&p_updates;
//这两种表达方式等效
  1. *运算符两边的空格
int* p1,p2;//该行程序会创建一个指针p1和一个int变量p2

5.指针和数字

int *pt;
pt=(int *)0xB8000000;

要将数字值作为地址来使用,应通过强制类型转换将数字转换为适当的地址类型

6.使用new来分配内存

我们将指针初始化为变量的地址,变量是在编译时分配的有名称的内存,而指针只是为可以通过名称直接访问的内存提供了一个别名—在运行阶段分配未命名的内存以存储值C++中仍然可以用库函数malloc()来分配内存,但C++中有更好的方法—new运算符

int nights=1001;
int * pn=new int;
*pn=1001;
cout<<"nights value="<<nights<<":location"<<&nights<<endl;
cout<<"int value="<<*pn<<":location"<<pt<<endl;

//nights value=1001:location 0028F7F8
//int value=1001:location 00033A98

1.在运行阶段为一个int值分配未命名的内存,并用指针来访问这个值
2.程序员告诉new要为哪种数据分配内存,new int告诉程序,需要适合存储int的内存,new根据类型来确定需要多少字节的内存。然后找到的话返回其地址,接下来,将地址赋给pn,pn是被声明为指向int的指针(即pn是地址,*pn是存储在那里的值)
3.指针pn和pd指向int和double这两个数据对象,有了这两个指针,就可以像使用变量那样使用 pn和 pd了,将值赋给pn和pd,从而将这些值赋给新的数据对象

7.用delete释放内存

当需要内存时,可以使用new来请求;使用完内存后,用delete归还给内存池,归还或释放的内存可供程序的其他部分使用,使用delete时,后面要加上指向内存块的指针

int * ps= new int;
...
delete ps;

8.用new来创建动态数组

使用new时,如果在运行阶段需要数组,则创建它,如果不需要,则不创建。还可以在程序运行时选择数组长度。这被称为动态联编

  1. 创建
int * psome=new int [10];//new运算符返回第一个元素的地址,该地址被赋给指针psome
delete [] psome;//方括号告诉程序,应该释放整个数组(如果使用new时不带方括号,则使用delete时也不应带方括号)

【注】
1.不要使用delete来释放不是new分配的内存
2.不要使用delete释放同一个内存两次
3.注意前后有无括号
4.对于空指针来说delete是安全的

  1. 使用
    由于psome指向数组的读一个元素,因此*psome是第一个元素的值,另:可将指针当做数组名使用。
double * ps=new double [3];
p3[0]=0.2;
p3[1]=0.5;
p3[2]=0.8;
cout<<"p3[1] is"<<p3[1]<<endl;p3[1] is 0.5
p3=p3+1;//指针指向数组p3的第二个值
cout<<"now p3[0] is"<<p3[0]//now p3[0] is 0.5

9.指针、数组和指针算术

指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式:

double wages[3]={10.0,20.0,30.0};
short stacks[3]={3,2,1};
double *pw=wages;
short *ps=&stack[0];

cout<<"pw="<<pw<<" *pw="<<*pw<<endl;
pw=pw+1;
cout<<"pw="<<pw<<" *pw="<<*pw<<endl;
//pw=0x28ccf0 *pw=10
//pw=0x28ccf8 *pw=20
cout<<"ps="<<ps<<" *ps="<<*ps<<endl;
ps=ps+1;
cout<<"ps="<<ps<<" *ps="<<*ps<<endl;
//ps=0x28ccea *ps=3
//ps=0x28ccec *ps=2
cout<<"stack[0]="<<stack[0]<<" stack[1]="<<stacks[1];
cout<<"*stack="<<*stack<<" *(stack+1)="<<*(stack+1)<<endl;
//stack[0]=3 stack[1]=2
//*stack=3 *(stack+1)=2
  1. 在多数情况下,C++将数组名解释为数组第一个元素的地址,将pw声明为指向double类型的指针,然后将它初始化 为wages
double *pw=wages;
wages=&wages[0]
  1. pw加1,这样数字的地址值将增加8(double类型),使得pw的值为第二个元素的地址;ps加1,这样数字的地址值将增加2(short类型)
  2. 将一个指针减去另一个指针(仅当两个指针指向同一个数组)获得两个指针的差,得到的是一个整数
int tacos[10]={5,2,8,4,1,2,2,4,6,8};
int *pt=tacos;
pt=pt+1;
int *pe=&tacos[9];
pe=pe-1;
int diff=pe-pt;//diff=7(tacos[8]-tacos[1])
  1. 数组表达式stack[1],C++将该表达式看作是*(stack+1)—先计算数组的第二个元素的地址,再找到存储在那里的值
  2. 数组名被解释为其第一个元素的地址,而对数组名应用地址运算符&时,得到的是整个数组的地址
short tell[10];
cout<<tell<<endl;//是一个2字节内存块的地址,tell+1将地址值加2—tell是short指针*short
cput<<&tell<<endl;//是一个20字节内存块的地址,&tell+1将地址值加20-&tell是包含20个元素的short数组(short*[20])
  1. 对指针解除引用意味着获得指针指向的值,例如p。另一种对指针解除引用的方法是使用数组表示法,pn[0]与pn是一样的
int *pt=new int [10];
*pt=5;//将第一个元素的值设为5
pt[0]=6;//将第一个元素重新设为6
pt[9]=44;
int coats[10];
*(coats+4)=12;//coats[4]设置为12

10.指针和字符串

数组名是第一个元素的地址,因此cout语句中的flower是包含字符r的char元素的地址。cout对象认为char的地址是字符串的地址,因此它打印该地址处的字符,然后继续打印后面的字符,直到遇到空字符为止。这里的关键在于flower是一个char的地址,而表达式“s are red\n”这一字符串像数组名一样,也是第一个元素的地址。

char flower[10]="rose';
cout<<flower<<"s are red\n";
char  animal[20]="bear";
const char * bird="wren";
char * ps;
cout<<animal<<"and"<<bird<<endl;
cin>>animal;//**fox**
ps=animal;
cout<<ps<<"!\n";//fox!
cout<<"before using strcpy():\n";
cout<<animal<<"at"<<(int *)animal<<endl;//fox at 0x0065fd30
cout<<ps<<"at"<<(int * )ps<<endl;//fox at 0x0065fd30

ps=new char[strlen(animal)+1];
strcpy(ps,animal);
cout<<"after using strcpy():\n";
cout<<animal<<"at"<<(int *)animal<<endl;//fox at 0x0065fd30
cout<<ps<<"at"<<(int * )ps<<endl;//fox at 0x004301c8

  1. 程序创建了一个char数组(animal)和两个指向char的指针变量(bird和ps),程序将animal数组初始化为字符串“bear”
  2. “wren”实际表示的是字符串的地址,因此这条语句将“wren”的地址赋给了bird指针。对于cout来说,使用数组名animal和指针bird是一样的,他们都是字符串的地址,cout将显示存储在这两个地址上的两个字符串。
  3. 一般来说,如果给cout提供一个指针,它将打印地址。但如果指针的类型为char*,则cout将显示指向的字符串。如果要显示字符串的地址,则必须将这种指针强制转换为另一种指针类型,如int*
ps=animal;
...
cout<<animal<<"at'"<<(int *)animal<<endl;

因此ps显示为字符串“fox”,而(int*)ps显示为该字符串的地址

  1. 经常需要将字符串放到数组中。初始化数组时,请使用=运算符;否则应使用strcpy()或strncpy()

11.使用new创建动态结构

struct inflatable
{
char name[20];
float volume;
double price;
};
int main()
{
inflatable * ps=new inflatable;
cin.get(ps->name,20);
cin>>ps->price;
} 
  1. 如果结构标识符是结构名,则使用句点运算符
  2. 如果标识符是指向结构的指针,则使用箭头运算符
struct A
{
int year;
};

int main()
{
A s1,s2,s3;
s1.year=1998;
A *pa=&s2;
pa->year=1999;
A trio[3];
trio[0].year=2003;
const A * arp[3]={&s1,&s2,&s3};
cout<<arp[1]->year<<endl;//1999
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值