打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
C (4) 基本数据类型和基础的复合类型

Cpt4 复合类型

4.1 数组

a. 用于相同类型变量的批量管理,定义格式为type Name[size]
size要求为编译阶段就可以确定的整型常量;
数组元素索引从0开始;
理论上下标不能越界,但编译器一般不会进行检查;
数组支持{}列表初始化(部分编译器可能要求数组为static类型);
通过sizeof查看数组大小是以字节作为单位;(即数组的大小而不是容量)
数组只能在定义时进行批量赋值(列表初始化,缺省补零);
int a[5]{}=int a[5]={}=int a[5]={0};

4.2 字符串(char数组)

a. 字符串包括C-style string和string类库字符串,
string的实现在C-style里采用的是char数组实现,从数组首个元素的指针开始向后读取直到遇到表示字符串结束的空字符’\0’(也就是说如果char数组本身不是以’\0’空字符结尾的话,就顺着内存地址向后输出直到遇到空字符);
对于char数组可以直接使用“字符串”初始化(末尾自动以’\0’结尾);
以’\0’结尾的字符数组和C-style的字符串等价,不过char数组可以设计的更大(缺省采用默认值’\0’);
对char a=“b”,系统首先构造一个临时的字符串常量'b’'\0’,将其首地址作为值赋给“b”常量,因此该赋值语句无效;
而对char a[],a实际上是字符数组首个元素的地址;
cout允许将字符串拆分输入,即cout”1””2”等价于cout”12”;
使用cstring头文件的strlen可以读取字符串长度(以字符为单位)(不包含’\0’);

b. cin读取字符串时以空白(空格制表符回车)作为'\0’,因此理论上单次cin只能读入一个单词到一个string变量里;

c. cin.getline(varName,size),按照size读取一行字符(以换行符作为结束,不保留换行符(表示为’\0’,输入缓冲的换行符被丢弃))到varName(字符数组);
cin.get(varName,size),调用和getline一致,但是输入缓冲的换行符不丢弃(保留换行符判断是否完整读取了),因此连续的cin.get之间需要通过cin.get()跳过一个空白的换行符;
cin.getline()不需要;
使用get超出长度范围的输出会被留在缓冲区等待下一个接受输入的流对象;getline超出长度范围会终止当前输入;(可能会报错)

4.3 string类

a. C++98标准引入string类,需要包含头文件string;
可以将string看作是一个自适应大小的char数组;
不同于char数组,string类之间允许变量式赋值;
string变量允许对+的重载,str1=str2+str3,表示str1是2和3的连接;
在string之前的字符串实现为char数组,其复制为strcpy,拼接为strcat(都依赖于头文件cstring);
需要注意C-type的char数组字符串操作不具有自适应性,更安全的函数是strncat和strncpy,引入参数n表示长度;
一般使用string类更方便;
字符串长度:string.size(),或strlen(char数组);

对于没有初始化的char数组和string对象,char数组仍然以首地址起到第一个’\0’的距离作为数组长度;string则表示为空;

cin>>string实际上是string类的一个友元函数表示;

b. 其他形式的字符串字面值
对字符数组表示,对应的在初始化数据前加上前缀L(wchar_t)、u(char16_t)、U(char32_t);
C++11补充了Unicode方案的UTF-8,字符可能存储为1-4个byte,采用前缀u8;
C++补充了原始字符串形式raw,在该形式中不具有转义字符形式,其字符串形式为R”()”;
(注意,R”()”实际上是一种基本形式,如果输入的字符串中包含”(或者)”这类,可以将格式符自定义为任意对称型,例如R”1()1”,称为自定义定界符,不允许包括空白、小括号等);

4.4 结构

a. 结构是基础的自定义变量类型,允许多个不同类型的成员;
结构采用struct关键字,使用前需要先定义该结构类型;
在C语言中使用结构类型创建变量时同样需要使用struct关键字表明结构身份,但C++允许省略使用过程中的struct;


定义结构时其成员之间使用分号间隔;初始化时使用逗号间隔;
对结构对象,使用成员运算符.访问其成员;

{}重载的赋值操作只允许用于初始化过程
C++使结构在性能上足够接近基础类型,即可以用作参数和返回值,且支持同类型的值传递

b. C++结构的定义和变量定义可以整合到一个过程中,
struct typeName{blabla}vary1,vary2;
可以同时对变量进行初始化,
struct typeNmae{}vary1={};;

c. 结构数组,对结构数组允许批量赋值或初始化,例如strType a[]={{},{}};

d. 结构中的位字段,结构允许包含指定位数的结构成员,例如其中的某个成员(位字段)可以定义为unsigned int SN:4,则SN是ui类型,占用4个字节;可以使用unsigned int:4这类无名成员表示固定间隔,在初始化过程和赋值过程会被跳过;

4.5 联合union,

可以理解成允许采用不同类型进行调用的一段内存,类似(typeName*)&variable;

a. 可以将联合理解为一段内存,其大小固定,类型可以变化;
其大小为最大成员的大小;
union一般用于节省空间,要求成员不同时有效(作用域和生存期不重合);
采用union关键字;
对于结构中的匿名union成员,union中的成员可以由struct直接调用;

4.6 枚举

a. enum 类型名{枚举成员列表,以逗号间隔};
enum枚举操作一般用来批量定义属于同一类的符号常量,例如用于提高switch语句易读性;
枚举成员可以被直接访问(而不需要经过变量类型名,可以理解成枚举成员被批量定义为了符号常量,以const作为实现);
枚举是这样一种类型,该类型的变量只能以其成员作为取值;但是实际上可以用long long给枚举成员赋值(要求不超出枚举类型允许的范围,一般取决于实现实际上给枚举类型分配的内存大小);
对于类型枚举本身进行重载的运算符只有=;

b. 枚举常量值,默认情况下以0开始;
可以在定义时对枚举成员进行初始化,缺省初始化的成员根据前边最近的初始化的成员的数值向后递增;
也就是可能出现不同枚举成员实际取值相同,对这种情况枚举变量实际上相等;

c. 强制类型转换式枚举赋值
C++允许通过强制类型转换对枚举对象赋值,要求数值在枚举类型的取值范围内(取值范围为,最大值为包含枚举成员值的最小2的幂-1,最小值为0或小于最小值的2的幂+1);
d. C++11扩展了枚举,增加了作用域内枚举;

4.7 指针和自由存储空间

a. 面向对象编程和传统的过程性编程的区别在于,OOP强调的是在运行阶段(而不是编译阶段)进行决策,这要求可以灵活的堆内存进行分配,(OOP使用new和动态内存分配实现这一功能;
对变量采用地址运算符&可以得到其内存地址,一般保存为16进制;
对于实现可能将变量顺序或倒序进行存储(因此实际变量的内存先后不能确定);
对于已知内存地址,可以按照特定格式的指针访问其值,通过使用间接值运算符* 按照对应的类型访问内存;
从实现意义上,a==p_a;p_a==&a

int* 看起来更直观;int* p_a,p_a是指向int的指针,类型是int*,p_a是一个int类型的变量*;intp_a也对*;

b.指针的声明和初始化,
int* p1,p2,实际上p1是int*,p2是int类型,也就是对每个指针变量名都需要使用一个*;
不允许指针间的强制类型转换
指针变量本身的长度可能取决于系统,且系统可以针对不同的类型使用不同长度的地址;

c. 指针的危险,声明指针而不对其进行初始化是危险的,这可能导致关键代码内存的数据被修改;
例如 int * pa; pa=10;pa实际指向的位置是不是安全并不能确定*;一定要在对指针应用解除引用运算符**之前,将指针初始化为一个确定的、适当的地址,这是关于使用指针的金科玉律;

d. 可以对指针进行强制类型转换,不推荐;

e. 动态地内存分配,指针的真正用武之地在于,在运行阶段分配未命名的内存存储数据,该数据地址只能通过对应的指针进行访问,因此具有动态的大小;
C++支持malloc,推荐new;
基本调用方式为,int* pa=new int;


动态内存和静态内存的数据对象存储在不同位置,静态内存对象一般存储在栈区,动态内存对象一般存储在堆区或自由存储区;
内存耗尽时,new返回空指针(异常);
内存释放,使用delete归还通过new分配的内存
int* pa=new int; delete pa;//直接delete指针变量名,但指针本身并没有被删除,可以看成是解除了指针变量和当前地址的连接关系;
一定要配对地使用new和delete,否则会导致内存泄漏;
不允许对已经释放过的内存进行再次释放;
不允许使用delete对new之外的内存地址进行释放;
对空指针NULL可以使用delete;

f. new动态数组,一般对于大型数据使用new;
new一般用于运行时刻的动态调整,以提高内存使用效率,降低平均开销;
new提供了一种动态联编操作,即允许在程序运行时设定数组的存在性、长度;
使用静态联编时必须在编译前指定数组长度使用动态联编时,数组的长度在运行时可以动态调整;
int * p_Ar=new int[10];
delete [] p_Ar;//使用[]说明是数组
实现会自动的记录所分配的动态数组的长度,以便于在delete时释放内存;
但这一长度数据是不公开的,因此不能通过sizeof读取;

4.8 指针、数组和指针算术

a. 在C和C++中,指针和数组基本等价;一个区别是可以对指针进行运算以便于特定数据格式的操作;
例如int*p;p+1后实际上是向后推了一个int的长度,因此此时的p[0]等价于实际的p[1];指针算数,指针变量以其指向类型作为运算的单位;
C++将数组名解释为地址;int a[10];a=&(a[0]);
也就是对任意地址变量,都允许使用[]以特定格式批量访问内存;
(stacks+1)和stacks[1]是等价的;
数组名和指针的区别是,指针允许进行修改,例如更换起始点位置;此外指针不能使用sizeof读取整体尺寸;

b. 注意区分数组地址和数组名,数组名实际上表示的是数组首个元素的地址,其类型为元素类型,也就是int a[10],a的类型是int指针,a+1向后推进一个int的长度;但对&a,&a是一个长度为10的int数组类型的指针,(&a+1)是a向后推进10个in的长度;


short (pas)[20]=&tell;//将pas设置为一个指向20个short的数组的指针,这里的变量类型是包含20个short的数组,长度为20short,其地址被赋予为tell的首地址,且pas与tell等价;
如果是short * pas[20],则表示一个short类型的数组,其中的每个元素都是short类型;
short (*a)[20]=&tell;
short *b=tell:
实际上a的地址和b的地址一致,但是a的格式尺寸是20short,b的格式尺寸是short;

c. 指针差运算一般用于计算元素间隔,例如对一个动态数组进行长度统计或者间隔统计;

d. 指针和字符串
给cout提供一个字符的地址,则cout将从该字符开始打印直到遇到空字符位置;
字符串和其引号“”整体被认为是一个char指针;
即cout对字符数组、字符串、字符常量都采用指针传递的形式,解释为字符串首个字符的地址;
基于这一点,对字符串类型的地址输出不能直接cout指针,而是应该将其强制类型转换为int等类型;
//报错,使用了未初始化的变量ps;

字符串常量一般被保存在预留空间,并关联到const char*指针;
总体来说禁止使用字符串常量或者未初始化的指针接收输入;

e. 字符串复制:
string a=”abc”;
char* b=new char[strlen(a)+1];
strcpy(b,a);
采用固定长度的数组进行strcpy或strncpy操作需要注意长度问题,需要给’\0’保留位置;超出长度可能导致溢出;
采用赋值运算符对字符串或字符数组进行操作实际上仍然是地址传递,字符串的实现只有一个;
string可以使用赋值运算符;

f. 在运行时创建数组优于在编译时创建数组,对于结构也是如此,同样采用new进行实现,该实现方式适用于类;
内存分配和基础类型一致;
优于new得到的对象没有名称,因此对于new得到的动态结构不能使用成员运算符.访问其成员,C++针对指针提供了箭头成员运算符,pointer->member;或者使用*(pointer)等价于结构名,即*(pointer).member;
g. 一个new和delete的例子,


h. 自动存储、静态存储和动态存储
自动存储,即常规变量,称为自动变量,其生存期为所属函数,作用域为所在代码块;采用栈存储,即LIFO,栈是一个连续的内存段;
静态存储,静态变量的生存期为整个程序,采用static关键字修饰或者采用将变量定义在函数外部的形式;早期C语言只支持初始化静态变量;
动态存储,基于new和delete,数据的生存期不取决于程序或函数,使用堆实现,存在内存不连续的问题;
内存泄漏:内存不能被访问也不能回收的情况;

4.9 类型组合

a. 对于比较复杂的自定义类型,构造指针建议使用auto;

4.10数组的替代品

a. 模板类vector类似string是一种动态数组,其实现是自动地使用new和delete实现对内存的管理;
vector要求包含头文件vector,使用命名空间std;(或者使用str::vector调用)
标准模版类vector声明:vector vectorName(num_elem);
b. 模板类array,可以看成是固定长度的vector,使用静态内存分配实现,需要包含头文件array,使用命名空间std::array;
标准模板类array声明:array<typeName,num_elem> arr;
num_elem不能是变量;
C++11允许对vector和array的列表初始化;
相比数组,array更安全;
实际上相比数组,array=支持使用成员函数at()进行访问,at()会对非法索引进行校验避免数组越界,但会导致运行时间变长(一般vector更常用);
此外还有边界函数begin()和end()以便于确定实际范围;
array支持对象到对象的复制;

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
C语言基础:数组的指针,指针数组,你真的会了吗
Delphi 中String类型原理介绍
C语言中sizeof与strlen区别
教你几招消灭代码漏洞的方法
C语言学习之基础知识点—指针!上万字的干货知识
C语言指针从入门到精通
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服