三、处理数据
OOP的本质是设计并扩展自己的数据类型(类)
内置的C++类型分为两组:基本类型和复合类型,其中本章介绍基本类型。
3.1简单变量
程序存储信息有三个基本属性:
- 信息存储位置
- 信息存储内容
- 存储信息类型
变量名:
- 有相对含义
- 只能使用字母、下划线和数字
- 第一个字符不能是数字
- 区分大小写
- 不能与关键字重名
- 以两个下划线、下划线和大写字母打头的名称被保留给实现使用,一个下划线开头的名称被保留给实现,作为全局标识符
- 一般来说my_student或者myStudent,也可以直接加个前缀来表明含义,nmyNum,n是整数值
- 保持一致性和精度,根据自己的需求和个人风格来定义
3.1.1整型
C++基本整型:char、short、int、long、long long,区分有符号版和无符号版
共有十种类型
short 、int 、long、long
计算机内存由‘位’单元组成,不同类型通过使用不同数目的位存储值,最多能表示四种不同的整数宽度。
short 至少16位;
int 至少与short一样长;
long至少32位,至少跟int一样长;
long long 至少64位,至少跟long一样长
💡:位、字节、字:是计算机数据存储的单元
位:是最小的存储单元,一个位存储一个1位的二进制码。八位就是2^8=256种不同组合,可以表示0-255或者-129-127
字节:一个字节由8位组成,是存储空间的基本计量单位,1个字节可以储存1个英文字母或者半个汉字。
字:由若干个字节构成,字的位数叫做字长,字通常为16、32或64个位组成。如果是一台16位机,那么,它的1个字就由2个字节构成,字长为16位。字是计算机进行数据处理和运算的单位。
KB:1024个字节
sizeof()运算符可以返回类型或变量的长度,单位为字节
#include <iostream>
#include <climits> //宏定义
using namespace std;
int main(void)
{
int n_int=INT_MAX;//climits
short n_short=SHRT_MAX;
long n_long=LONG_MAX;
long long n_llong=LLONG_MAX;
cout<<"int:"<<sizeof(n_int)<<"字节"<<endl;
cout<<"short:"<<sizeof(n_short)<<"字节"<<endl;
cout<<"long:"<<sizeof(n_long)<<"字节"<<endl;
cout<<"llong:"<<sizeof(n_llong)<<"字节"<<endl;
cout<<"int:"<<INT_MAX<<endl;
return 0;
}
3.1.2无符号类型
优点:扩大存储的最大值
用关键值unsigned来修改声明即可
unsigned short change;
unsigned int rovert;
#define ZERO 0;//预处理语句,使ZERO恒为0
根据需要自己进行选择类型。
3.1.3整形字面值
字面整数值,采用多少进制
C++使用前一(两)位来标识数字常量的基数。
第一位1-9=>十进制
第一位0,第二位1-7=>八进制
前两位位0x或0X,十六进制
💡:默认情况下,cout以十进制格式显示整数,不管在程序中怎么书写。想更改的话,使用控制符dec、hex、oct,分别为十进制、十六进制、八进制
3.1.4char类型、字符和小整数
编程语言通过使用字母的数值编码解决存储字母的问题。最常用的符号集是ASCII字符集,例如字符A编码为65。有很多,本文用ASCII。
字符对应整数,数值编码。
cout.put()//成员函数
//重要的OOP概念
//类定义了如何表示和控制数据,成员函数归类所有,描述了操纵类数据的方法
转义序列编码(是一个字符)
字符名称 | ASCII符号 | C++代码 |
---|---|---|
换行 | NL(LF) | \n |
问号 | CR | ? |
单引号 | ’ | \’ |
wcha_t,扩展字符集
3.1.5bool类型
非零值为真true 5 -5
零值为假false 0
bool start=0;
int ans=ture;
//ans=1
3.2const限定符
常量的符号名称,如果程序在多个地方使用同一个常量,在需要修改该常量时,只需要修改一个符号定义即可。#define ZERO 0;
使用const关键字修改变量声明和初始化
const int Months = 12;//后面就不能够修改了,一般首字母大写,以提醒它是个常量
3.3浮点数
第二组基本类型,能表示带小数部分的数字。
计算机将这样的值分为两部分存储,一部分表示值,一部分用于对值进行放大或缩小,缩放因子,由此得浮点数
浮点类型:按照他们可以表示的有效数位和允许的指数最小范围来描述
- float 至少32位
- double至少48位,不少于float,通常64位
- long double同村80、96、128位
浮点常量:
double直接写,float类型后面加一个f或者F后缀
3.4C++算数运算符
加减乘除、求模
+
—
*
/
%
运算符优先级
算数运算符遵循:先乘除、后加减,可以使用括号执行自己定义的优先级
优先级相同的时候,看操作数的结合性是从左到右还是从右到左
类型转换
-
将一种算术类型的值赋给另一种算术类型的变量时,C++将对值进行转换
-
表达式种包含不同的类型时,C++将对值进行转换
-
将参数传递给函数时,C++将对值进行转换
{}列表初始化
🌟auto关键字,不指定变量的类型,编译器将把变量的类型设置为与初始值相同。
auto n=100;//n为int auto x=1.5;//x为double
四、复合类型
数组、字符串、结构、共用体、枚举、指针、new delete动态内存、动态数据、动态结构,vector、array类
4.1数组
数组(array)是一种数据格式,能存储多个同类型的值。
例如,60个int类型的值,每个值都存储在一个独立的数组元素中,计算机在内存中依次存储数组各个元素。元素之间是连续排布
💕创建数组:使用声明语句,
- 元素类型 2. 数组名 3.数组中元素个数
short months[12];//创建数组名为months 12个元素 short类型
typeName arrayName[arraySize];//arratSize是整形常熟、const值、常量表达式
float loans[20];//类型是float数组
复合类型:使用其他类型来创建的
数组:可以单独访问数组元素,方法是使用下标或索引来对元素进行编号。C++数组是从0开始编号。
months[0];//是months数组的第一个元素
months[11];//是months数组的最后一个元素
最后一个元素比个数-1
💕数组初始化:
int yams[3];
yams[0]=1;
yams[1]=2;
yams[2]=3;
int yams1[3]={20,10,80};//
int yams2[]={1,2,3};
int yams3[10]={};//把所有元素设置为0
后面可以使用for循环处理数组,sizeof,数组名是字节数、元素得到的是元素长度
4.2字符串
字符串是存储在内存的连续字节中的一系列字符。
C++处理字符串的方式有两种,第一种来自C语言,另一种基于string类库的方法。
4.2.1c风格
char数组,每个字符位于自己的数组元素中。以空字符结尾,空字符被写作\0,ASCII码为0
char dog[3]={'b','e','a'};//不是字符串
char dog[3]={'f','a','\0'};//是字符串
char bird[11]={"Mr.Cheeps"};//字符串常量,双引号隐式包括结尾的空字符,不用显示的包括它,尽量让数组长一点,还要留一个空字符位置
//单引号是字符,双引号是字符串有空字符的
//""代表地址,两者不是一回事
拼接字符串常量
std::cout<<"hello"
"hi";
在数组中使用字符串
#include <iostream>
using namespace std;
int main(void)
{
const in size=15;
char name1[size];
char name2[size]="hello";
cin>>name1;
strlen(name1);//字符串长度,比如hello是5,而不是6
sizeof(name1);//sizeof是整个数组的长度,我们定义的size 15
return 0;
}
🌟🌟cin使用空格、制表符和换行符确定字符串的结束位置,后面的放到缓冲区,在输入在缓冲区中进行。
所以需要一个方法读取整行的字符串数据:
面向行的输入:getline()、get()
两个函数都读取一行输入,直到遇到回车
getline()
cin.getline(name,20);//数组名称、读取的字符数,最多读取19个字符
//getline在读取指定数目字符或遇到换行符时停止读取,不保存 换行符,存储字符串时,用空字符来替换换行符
get()
cin.get(name,ArSize);//get不在读取并对其换行符,而是将其留在输入队列中
cin.get(name,ArSize);
cin.get();//调用可读取下一个字符,即使是换行符
cin.get(dessert,ArSize);
cin.get(name,ArSize).get();//cin.get(name,ArSize)返回一个cin对象,继续调用get()函数
当读取空行时,会导致下面的输入被阻断。
cin.clear();//恢复输入
混合输入字符串和数字
getline()不行,因为会把前面cin的换行符看成空行,所以应该先读取并丢弃换行符
cin>>year;
cin.get();
cin.getline(address,20);
4.2.2string类库
一种数据类型,可以使用string类型对象 而不是字符数组来存储字符串。
使用string类,必须在头文件包含string,她位于命名空间std里面。
#include <string>
using namespace std;
int main()
{
string str1;//类设计可以让程序自动处理string大小
string str2="panther";
//可以使用数组表示下标也来访问string中的字符
str2[2];
str2
return 0;
}
这样就string对象声明就是简单变量,而不是数组啦!
string操作
不能将一个数组赋值为另一个数组,但string对象可以赋给string对象
//赋值
string str1;
string str2="panther";
str1=str2;
//合并
string str3;
str3=str1+str2;
str1 += str2;//str1的末尾追加str2的内容
//长度
int len1=str1.size();//string是一个类,str1是类的一个对象,size()是成员函数
/*
C风格复制和附加到末尾
*/
#include <cstring>
strcpy(charr1,charr2);//将字符串复制到字符数组中
strcopy(charr1,charr2);//将自己付附加到字符串末尾
int len2=strlen(charr1);
//对比下来string很简单,所以应用string类
string类I/O
捕获字符串输入,读取单词和读取有空格之类的一行时,语法不一样;
getline(cin,str1);//输入流存放的str字符串中
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int main (void)
{
char charry[20];
cout<<"输入前charry字符串数组长度:"<<strlen(charry)<<endl;//数组未被定义,随机
string str1;
cout<<"输入前str类长度:"<<str1.size()<<endl;//未被初始化自动为0
//捕获,字符串数组
cout<<"请输入一行"<<endl;
cin.getline(charry,20);//cin相当于istrean类的一个对象,getline()是成员函数
cout<<"输入的charry:"<<charry<<endl;
//捕获,string
cout<<"请输入一行"<<endl;
getline(cin,str1);
cout<<"输入的str1:"<<str1<<endl;
return 0;
}
4.3结构简介
结构、结构体
篮球运动员:姓名、工资、身高、体重、平均得分、命中率、助攻次数。
希望有一种数据格式可以将所有信息存储在一个单元中。也就是存储多个不同类型的元素。
结构可以满足要求,可以存储多种类型的数据,如果是一个球队,可以使用结构数组,也是C++ OOP的基石。
struct inflatable //自定义结构体类型inflatable,等同于int类型
{
//结构存储的数据类型的列表
char name[20];//结构成员
float volume;
double price;
}
//创建inflatable变量
inflatable hat;
inflatable woopie_cushion;
//使用.来访问结构成员
hat.price;//double型
在程序中使用结构
#include <iostream>
#include <string>
using namespace std;
//一般结构体放main函数前面,外部声明可以被其后面任何函数使用
//C++提倡外部结构声明,内部变量定义
struct inflatable
{
/* data */
char name[20];
float volume;
double price;
string value;//也可以使用string类
};
int main(void)
{
/* code */
//定义一个变量并初始化
inflatable guest={"guest",1.88,29.6};//,分隔值列表,并用花括号括起
inflatable hat={"hat",1.3,4.5};
cout<<"显示内容"<<guest.name<<" and "<<hat.name<<endl;
cout<<"price "<<guest.price+hat.price<<endl;
return 0;
}
其他结构属性
与内置类型尽可能相似,可以将结构作为参数传递给函数,也可以让函数返回一个结构。也可以使用赋值运算符=将结构赋给另一个同类型的结构。将变量名放在结束括号后面即可直接创建结构变量同时初始化。
#include <iostream>
using namespace std;
//一般结构体放main函数前面
struct inflatable
{
/* data */
char name[20];
float volume;
double price;
}in_1,in_2={"hh",1.2,5.6};//将变量名放在结束括号后面即可直接创建结构变量,甚至可以进行初始化
int main(void)
{
/* code */
in_1=in_2;//赋值
cout<<"赋值 "<<in_1.name<<endl;
return 0;
}
struct
{
int x;
int y;
}position;//struct可以没有名字,直接生成一个结构体对象,但是只能创建这一个变量了
4.4结构数组
创建元素为结构的数组,比如说篮球队,方法和创建基本类型数组完全相同。
inflatable gift[20];
gift[0].volume;//gift本身是一个数组,不是结构,要使用成员需要指定数组中的哪一个元素
//初始化
inflatable guests[2]=
{
{"hh",1.2,3.2};
{"zz",1.2,4.3}
};
结构中的位字段
指定占用特定位数的结构成员
struct torgle
{
unsigned int SN :4;//占四个比特
bool GoodIng:1;//占一个比特
};
//一般用在硬件设备上的寄存器
4.5共用体
共用体(union)是一种数据格式,能够存储不停的数据类型,但只能同时存储其中的一种类型。
结构体可以同时存储int、long、double,共用体只能存储int、long、double中的一种。
用途:当数据使用两种或更多种格式,但不会同时使用,可以节省空间,比如商品的id,有时是数字,有时是字符串。
union one4all
{
int int_val;
long long_val;
double double_val;
};
4.6枚举
C++的enum工具提供了另一种创建符号常量的方式,可以代替const。还允许定义新类型,但必须按严格限制进行。
🌟为了连续多个符号常量赋值,为了使用值
enum spectrum {red,orange,yellow,green,blue,violet,indigo};
//spectrum 新类型名称
//将red、orange、yellow等作为符号常量,对应于整数值0-6
//red=0的符号常量
对枚举,只定义了赋值运算。
意义:
- 1)C++ 中会使用const或者#define定义整型常量,当整型常量有多个且之间的值的全部或部分有递加的时候,定义起来稍显繁琐,此时用枚举显得很简洁:
//使用const:
const int MON =1;
const int TUE=2;
const int WED=3;
const int THU=4;
const int FRI=5;
const int SAT=6;
const int SUN=7;
//使用#define//定义一个整型变量,为整型变量赋以下值
#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
#define SUN 7
//使用枚举//定义一个枚举变量,此变量可以具有多个可能的值,枚举类型中数据都是整型且默认从0开始
typedef enum weekDay{
MON=1, //枚举类型中数据都是整型且从0开始,此处将第1个值设为1,则TUE , // 以下均从1开始递加
TUE,
WED, //C++中逗号不是一条语句,不是一条语句就可以用回车分行
THU, //这样有助于写注释
FRI,
SAT=7, //可以再重新赋值,此时SAT=7,而不是6
SUN //SUN=8,而不是7
}week_day;
week_day week=SUN;
注意:枚举类型是一种自定义的类型,故其形式和定义方法跟struct十分类似:
//注意:枚举变量的值只能等于定义好的那几个值。
————————————————
版权声明:本文为CSDN博主「modi000」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/modi000/article/details/80680960
设置枚举量的值
enum bits{one=1,two=2,dour=4,eight=8};
//指定的值必须是整数,或者指定一个,后面递增
枚举类型的取值范围
赋给枚举变量合法值
4.7指针和自由存储空间
1.计算机程序存储数据时必须跟踪三种属性
- 信息存储位置
- 存储的值为
- 存储的信息是什么类型
2.指针是一个变量,其存储的是值的地址,而不是值本身。
常规变量的地址,只需对变量应用地址运算符 (&),就可以获取它的位置
int a;&a; &取址运算符
3.指针策略是C++内存管理编程理念的核心。
处理存储数据的新策略刚好与之前的相反,将地址视为指定的量,值视为派生量。
指针用于存储值的地址,指针名代表的是地址。*运算符为取值运算符,将其应用于指针,可以得到该地址存储的值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7trnF8XW-1667267286297)(C:\Users\12285\OneDrive\yxh\C++学习\figure\4.7符号关系.png)]
#include <iostream>
using namespace std;
int main(void)
{
int updates=6;
int *p_updates;//加*的是指针变量的名字,前面跟着类型,不是取值,定义一个指针变量
p_updates=&updates;//指针里永远放的是地址
cout<<"value:updates="<<updates<<endl;
cout<<"value:*p_updates="<<*p_updates<<endl;//取值
cout<<"address:updates="<<&updates<<endl;
cout<<"address:p_updates="<<p_updates<<endl;
*p_updates=*p_updates+1;
cout<<"now:updates="<<updates<<endl;
return 0;
}
所以int变量updates和指针变量p_updates只不过是同一枚硬币的两个面updates表示值,使用&获得地址;p_updates表示地址,*获得值,p_updates指向updates,因此 *p_updates和updates完全等价。
4.7.1声明和初始化指针
//指针声明需要指定指针指向的数据的类型
int *p_updates;//p_updates是指针,*p_updates是int
//*两侧空格,没区别
int *p;//c风格
int* p;//c++风格
int *p1,p2;//只有p1是指针
int *p1,*p2;//两个指针
//指针初始化
int higgens=5;
int *p=&higgens;//将p初始化为higgens的地址
4.7.2指针的危险
将指针初始化为一个确定的、适当的地址。
4.7.3指针和数字
指针不是整型,虽然地址常作为整数处理,但截然不同,整数时可以执行加减乘除等运算的数字,而指针描述的是位置,位置做乘法没有任何意义,所以不能简单地将整数赋给指针。
int *p;
p=0xB8000000;//错误!!,整型数赋值给指针,出问题
//要加上强制的类型转换才行
p=(int *)0xB8000000;//两边都是地址,这样才有效
4.7.4使用new来分配内存
如何实现在程序运行时分配内存
以前都是将指针初始化为变量的地址,变量是在编译时分配的有名称的内存,指针只是为可以通过名称直接访问的内存提供了一个别名。
指针真正的用处在于,在运行阶段分配未命名的内存以存储值,这样只能通过指针访问内存
c++种,使用new运算符来分配内存
int *p=new int;//告诉new,为int型分配内存,返回内存块的地址
#include <iostream>
using namespace std;
int main(void)
{
int night=1001;
int *pt=new int;
*pt=1001;
cout<<"night value="<<night<<endl<<"night address="<<&night<<endl;
cout<<"int value="<<*pt<<endl<<"int address="<<pt<<endl;
return 0;
}
new分配的内存块通常与常规变量声明分配的内存块不同,变量的值存在栈的内存区域中,而new 在堆、自由存储区的内存区域分配内存
指出了声明指针所指向的类型原因,地址本身只指出了对象存储地址的开始,而没有指出其类型。指出类型后,才可以提供类型或长度信息。
内存耗尽
计算机可能会没有足够的内存而无法满足new的请求,new会引发一场异常,c++中,值为0的指针被称为空指针,c++确保空指针不会指向有效的数据,C++提供了检测并处理内存分配失败的工具。
4.7.5使用detele释放内存
当需要内存时,可以使用new来请求,另一个方面是delete运算符,在使用完内存后,能将其归还给内存池,这是通向最有效地使用内存的关键一步,归还或释放的内存可供程序其他部分使用。delete后面要加上指向内存块的指针。
int *p=new int;
delete ps;
//将释放ps指向的内存,但不会删除指针ps本身,可以将ps重新指向另一个新分配的内存块。
//一定要配对使用new和delete;delete的只能是new分配的内存,否则会发生内存泄露,也就是说,被分配的内存再也无法使用了,如果内存泄漏严重,程序将由于不断寻找更多内存而终止。
//不能释放已经释放的内存块
//不要创建两个指向同一个内存块的指针
4.7.6使用new来创建动态数组
对于大型数据,比如说数组、字符串和结构,应使用new。比如,编写程序,它是否需要数组取决于运行时用户提供的信息,如果通过声明创建数组,则在程序被编译时将为他分配内存空间。-静态联编。
但是用new,正运行阶段需要数组,则创建它,不需要则不创建,还可以在程序运行时选择数组的长度。-动态联编。使用静态联编时,在编写程序时指定数组长度,动态联编,在运行时确定数组长度。
//**1.使用new创建动态数组**
int *psome=new int[10];//返回第一个元素的地址
delete [] psome;
//2.使用动态数组
psome[0];//把指针当作数组名使用即可 第一个元素
psome[1];//第二个元素
//区别:
psome=psome+1;//数组名不能修改,但是指针是变量可以修改值,+1的效果会导致它psome[0]指向第二个元素?
//相邻的int地址通常相差2个字节,而将psome+1后,它指向下一个元素,表明指针算术有特别的地方
4.8指针、数组和指针算术
指针和数组基本等价。C++将数组名解释为地址。
整数变量+1,值加1;
4.8.1指针的算术运算
指针变量+1,增加的量等于它指向的类型的字节数,short指针+1,指针值+2;
#include <iostream>
using namespace std;
int main(void)
{
double wages[3]={10000.0,20000,30000};
short stacks[3]={3,2,1};
//数组名是数组的首地址
double *pw=wages;
short *ps=stacks;
cout<<"pw="<<pw<<",*pw="<<*pw<<endl;
pw=pw+1;
cout<<"add 1:"<<"pw="<<pw<<",*pw="<<*pw<<endl;
cout<<"ps="<<ps<<",*ps="<<*ps<<endl;
ps=ps+1;
cout<<"add 1:"<<"ps="<<ps<<",*ps="<<*ps<<endl;
cout<<sizeof(wages)<<"\n"<<endl;\\24字节 wages不代表地址,代表数组
cout<<sizeof(pw)<<"\n"<<endl;\\4字节32位 pw指针,指针长度
return 0;
}
stack[1]C++编译器将该表达式看作是*(stacks+1),也就是先计算数组第二个元素的地址,在找到存储在那里的值
区别就是可以修改指针的值,而数组名是常量
数组应用sizeof运算符得到的是数组长度,指针应用sizeof得到的是指针的长度
常见的笔面题目
🌟数组名被解释为第一个元素的地址,而对数组名应用地址运算符时,得到的是整个数组的地址。tell=&tell[0]❓⁉️数字上看地址相同,但概念上说&tell[0]是两字节内存块地址,&tell是一个20字节内存块的地址,所以tell+1=》地址加2;&tell+1=》地址加20
4.8.2指针和字符串
char flower[10]="rose";
cout<<flower<<"s are red"<<ednl;//rose
flower是数组名,但是一个char的地址,cout提供一个字符的地址,将从该字符开始打印,直到遇见空字符为止。
cout<<“haha”<<endl;这种,是把haha字符串常量的首地址传给cout,一直打印直到空字符。
#include <iostream>
#include <cstring>
using namespace std;
int main (void)
{
char animal[20]="bear";//字符串
const char *bird="wren";//指向常量的字符型指针,不能修改
char *ps;//未被初始化
cout<<animal<<" and "<<bird<<endl; //bear and wren
cout<<"Enter a kind of animal:\n";
cin>>animal;//bird,不能cin>>ps;因为ps未被初始化无处所指
ps=animal;//animal首地址给ps指针
cout<<ps<<endl;//bird
cout<<"Before using strcpy():\n"<<animal<<" at "<<(int *)animal<<endl;//bird at 0x61fdf0
cout<<ps<<" at "<<(int *)ps<<endl;//bird at 0x61fdf0
//ps 开创动态字符串数组内存,strlen(animal)://字符串长度 没有空字符,bird+1=4
ps=new char[strlen(animal)+1];
strcpy(ps,animal);//将字符串复制到字符数组中
cout<<"After using strcpy():\n"<<animal<<" at "<<(int *)animal<<endl;//
cout<<ps<<" at "<<(int *)ps<<endl;
delete [] ps;
return 0;
}
🌟一般来说,如果给cout提供一个指针,他将打印地址,但如果指针类型为char*,则cout将显示指向的字符串。若要显示字符串地址,则必须将这种指针强制转换为另一种指针类型,如int *
4.8.3使用new创建动态结构
在运行时创建数组优于在编译时创建数组,对于结构也是如此,使用new运算符在程序运行时为结构分配所需的空间。
->箭头成员运算符
将new用于结构由两步组成:创建结构&访问其成员,要创建结构,需要同时使用结构类型和new。
//创建一个未命名的inflatable类型,并将其地址赋给一个指针
inflatable *ps = new inflatable;
访问成员,创建动态结构时,不能将成员运算符句点用于结构名,因为这种结构没有名称,只知道地址
C++专门提供了一个运算符:箭头成员运算符(->),用于指向结构的指针,就像点运算符号用于结构名一样
例如,如果ps指向一个inflatable,则ps->是被指向的结构的price成员
也就是说:点运算符要配合结构体名称使用,箭头运算符要配合结构体指针
struct things
{
int good;
int bad;
};
things grubnose ={3,453};
things *pt =&grubnose;
grubnose.good;grubnose.bad;
pt->good;pt->bad;
如果结构标识符是结构名,则使用句点运算符,如果标识符是指向结构的指针,则使用箭头运算符
另一种访问结构成员的方法是,如果ps是指向结构的指针,则*ps就是被指向的值-结构,由于 *ps 是一个结构,因此( *ps).price是该结构的price成员,C++的运算符优先规则要求使用括号。
#include <iostream>
using namespace std;
struct food
{
char name[20];
int number;
double price;
};
int main(void)
{
//创建结构体指针,指向新开辟的内存空间
food *ps =new food;
//赋值
cout<<"Enter name of food item:"<<endl;
cin.get(ps->name,20);//cin.get()是针对char类型的,cin遇到空格就读取结束,但是cin.get(name,20)可以读取空格
cout<<"Enter number of food item"<<endl;
cin>>(*ps).number;
cout<<"Enter price of food item"<<endl;
cin>>ps->price;
cout<<"Name:"<<ps->name<<" Number:"<<(*ps).number<<" Price:"<<ps->price<<endl;
delete ps;
return 0;
}
1.一个使用new和delete的示例
使用new和delete来存储通过键盘输入的字符串的示例
假设程序要读取1000个字符串,其中最大的字符串包含79个字符,而大多数字符串都很短,如果用char数组来存储,则需要1000个数组,其中每个数组长度为80个字符,总共需要80000个字节,而其中很多内存没有被使用,另一种方法是,创建一个数组,它包含1000个指向char的指针,然后用new根据每个字符串的需要分配相应的内存,将节省几万个字节。
#include <iostream>
#include <cstring>
using namespace std;
char *getname(void);//声明
int main(void)
{
char *name;
name=getname();
cout<<name<<" at "<<(int *)name<<"\n";
delete [] name;//释放掉
return 0;
}
//捕获键盘输入的字符串并存起来
char *getname(void)
{
char temp[80];
cout<<"Enter last name:";
cin>>temp;
char *pn=new char[strlen(temp)+1];//空字符 以及cstring头文件
strcpy(pn,temp);
return pn;
}
4.8.5自动存储、静态存储和动态存储
根据用于分配内存的方法,C++有三种管理数据内存的方式:自动存储、静态存储和动态存储(有时也叫做自由存储空间或堆) 在后面还有一种线程存储,详情请见第9章
在存在时间的长短方面,以这3种方式分配的数据对象各不相同。
1.自动存储
在函数内部定义的常规变量使用自动存储空间,被称为自动变量,意味着当他们所属函数被调用时自动产生,函数结束时自动消亡,比图上文中的temp[]
自动变量是一个局部变量,其作用域为包含它的代码块。在某个代码块定义了一个变量,则该变量仅在程序执行该代码块中的代码时存在。{}
自动变量通常存储在栈内,意味着执行代码时,变量依次加入栈种,离开时,按相反顺序释放,这被称之为后进先出(LIFO),因此,在程序执行的过程中,栈将不断地增大和缩小。
2.静态存储
静态存储是整个程序执行期间都存在的存储方式,使变量称为静态的方式有两种,(1)在函数外面定义它(2)在声明变量时使用关键字static;在第九章
static double free=56;
自动存储和静态存储的关键在于:这些方法严格的限制了变量的寿命。变量可能存在于程序的整个生命周期-静态变量,也可能只在特定函数被执行时存在-自动变量。
3.动态存储
new和delete运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在C++中被称为自由存储空间或者堆。该内存池同用于静态变量和自动变量的内存是分开的。程序员对内存有更大的控制权,但内存管理也更复杂了。栈中,内存总是连续的,但new和delete的相互影响可能导致占用的自由存储区不连续。但new delete相互影响可能会导致占用的自由存储区不连续,使得跟踪新分配的内存位置更困难。
栈、堆和内存泄漏
new和delete没有配备使用,导致内存泄漏,C++智能指针有助于自动完成这种任务。
4.9类型组合
本章介绍了数组、结构和指针,可以各种方式组合它们,下面介绍其中的一些 ,从结构开始:
struct antarctica_years_end
{
int year;
};
//创建这种类型的变量
antarctica_years_end s1,s2,s3;
//使用成员运算符访问其成员
s1.year=1998;
//可以创建指向这种结构的指针
antarctica_years_end *pa=&s2;
pa->year=1999;
//创建结构数组
antarctica_years_end trio[3];
trio[1].year=2012;//trio是一个数组,trio[0]是一个结构,rio[1].year是结构的一个成员
//由于数组名是一个指针,也可以使用
(trio+1)->year=2003;
//创建结构数组
const antarctica_years_end *arp[3]={&s1,&s2,&s3};
arp[1]->year
//创建指向上述数组的指针,两个** 指向指针的指针
const antarctica_years_end ** ppa=arp;
//ppa是指向结构指针的指针,*ppa是一个结构指针
(*ppa)->year
//C++11版本
auto ppb=arp;
4.10数组的替代品
模板类vector和array是数组的替代品
4.10.1模板类vector
类似于string类,也是一种动态数组,可以在运行阶段设置vector对象的长度,可在末尾附加新数据,也可以在中间插入新数据,基本上,它是使用new创建动态数组的替代品。实际上,vector类确实使用new和delete来管理内存,但这种工作是自动完成的
#include <vector> //必须加头文件
//vector包括在std命名空间中 std::vector
//模板使用不同的语法来指出它存储的数据类型,以及指定元素数
using namespace std;
vector<int> vi;//vector<int>对象
int n;
cin>>n;
vector<double> vd(n);//vector<double>对象
4.10.2模板类array(c++)
vector类的功能比数组强大,但付出的代价是效率稍低,如果需要长度固定的数组,使用数组更佳,但不是那么方便和安全,所以C++11新增了模板类array,他也位于命名空间std中,array对象长度也是固定的,也是有栈(静态内存分配),而不是自由存储区,因此其效率与数组相同,但更方便,更安全。
#include <array>//必须包含头文件array
using namespace std;
array<int ,5> ai;
array<double,4> ad={1,2,3,4};
//C++11中,可以将列表初始化用于vector和array对象
4.10.3比较数组、vector和array
数组和array存储在相同的内存区域(栈)里面,vector存储在另一个区域(自由存储区或堆),可以将一个array对象赋给另一个array对象,而数组,必须逐元素赋值数据。
vector和array的成员函数at()
a2.at(1)=2.3;