C++学习笔记之四 复合类型1

1、数组

声明格式:float loans[20];

loans的类型不是“数组”,而是“float 数组”。这强调数组是使用float类型创建的。其中,方括号中的元素个数必须为整型常数或const值,也可以是常量表达式(如8*sizeof(int)),但不能是变量,因为变量的值是在程序运行时设置的。

需要注意的是:C++数组从0开始编号,C++使用带索引的方括号表示法来指定数组元素。例如months[0]是months数组的第一个元素,months[11]是最后一个元素。注意,最后一个元素的索引比数组长度小1.

(1)有效下标值的重要性

编译器不会检查使用的下标是否有效。例如,如果将一个值赋给不存在的元素months[101],编译器并不会指出错误。但是程序运行后,这种赋值可能引发问题,它可能破坏数据或代码,也可能导致程序异常终止。所以必须保证程序使用只使用有效的下标。

(2)初始化数组

int yamcosts[3] = {20, 30, 5};

只需提供一个用逗号分隔的值列表(初始化列表),并将它们用花括号括起来即可。如果没有初始化函数中定义的数组,则其元素值将是不确定的。这意味着元素的值为以前驻留在该内存单元中的值。

当数组中元素数目与初始化器中值的数目不想同时情况。只有在定义数组时才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组:

int cards[4] = {3, 6, 8, 10};

int hand[4] ;

hand[4] ={5, 6, 7, 9};        //这种是不正确的

hand = cards;                 //不正确

要想给数组赋值,必须使用下标分别给数组中的元素赋值。如果只对数组的一部分进行初始化,则编译器将把其他元素设置为0。

2、字符串

字符串是存储在内存的连续字节中的一系列字符。C++处理字符串的方式有两种。一种是来自C语言,常被称为C-风格字符串(C-style string)。另一种是基于string类库的方法。

(1)C-风格字符串

存储在连续字节中的一系列字符意味着将字符串存储在char数组中,其中每个字符都位于自己的数组元素中。C-风格的字符串有一种特殊的性质:以空字符结尾,空字符被写为\0。其ASCII码为0,用来标识字符串的结尾。例如下面两个字符串:

这两个数组中,只有第二个是字符串。空字符对C-风格字符串而言至关重要。C++有很多处理字符串的函数,其中包括cout使用的那些函数。它们都逐个的处理字符串中的字符,直到到达空字符为止。

例如上面两个数组,如果用cout输出字符串,第二个数组输出到\0就结束了,所以会输出7个字符;但是第一个数组并没有\0字符,所以cout会一直输出后面内存中的字符,直到遇到\0为止。虽然也很容易会有\0存在,但是还是不应该将不是字符串的字符数组当作字符串来处理。

如果像上面那样用{}和‘’处理字符串,工作会变得很麻烦。不必担心,有一种更好的、将字符数组初始化为字符串的方法----只需使用一个用引号括起来的字符串即可,这种字符串被称为字符串常量或字符串字面值。如下所示:

char bird[11] = "Mr.  Cheeps";

char fish[]  = "Bubbles";

用引号括起来的字符串隐式的包括结尾的空字符,因此不用显式的包括它。

①关于字符串的长度:

注意区分两个运算符:sizeof运算符指出整个数组的长度,比如说char cat[12] = "hello";,用sizeof(cat)得到的是12,即数组的长度;但是使用strlen函数返回的是存储在数组中的字符串的长度,而不是数组本身的长度,而且不包括空字符在内,所以上例strlen(cat)得到的是5,即hello的长度。

②关于字符串的输入:

字符串的输入存在两个缺陷,第一个缺陷是cin使用(空格、制表符和换行符)来确定字符串的结束位置;第二个缺陷是输入的字符串可能比目标数组长。

先看第一个例子:

来看一下这个例子的输出结果:

看看有什么问题呢?cin在获取字符串数组输入时只读取一个单词,读取该单词之后cin把该字符串放到数组中,并自动在结尾添加空字符。

这个例子的结果是cin把Alistair作为第一个字符串,并把它放到name数组中,把Dreeb留在输入队列中。当cin在输入队列中搜索用户喜欢的甜点时,它发现了Dreeb,因此cin读取Dreeb,并将它放在dessert数组中。

第二个缺陷的例子是cin不能防止将包含30个字符的字符串放到20个字符的数组中。

后面章节会介绍到cin的高级功能会解决这两个问题,大家不要着急。

③每次读取一行字符串输入

每次读取一个字符串显然不是很好的选择,比如程序要求输入城市名New York,使用cin得到的只是第一个单词New,显然这并不是你想要的完整的城市名。要想将整条短语而不是单词作为字符输入,需要采用另一种字符串读取方法。具体地说,需要采用面向行而不是面向单词的方法。

istream中的类正好提供了这样的两个函数:getline()和get()。这两个函数都读取一行输入,直到到达换行符。然而,随后getline()将丢弃换行符,而get()将换行符保留在输入队列中。下面将分别介绍这两个函数。

a、面向行的输入:getline()

getline()函数读取整行,使用回车键输入的换行符来确定输入结尾,但是并不将换行符保存到队列中。要调用这种方法,可以使用cin.getline()。

例如:假设使用getline()将姓名读入到一个包含20个元素的name数组中,可以使用这样调用:

cin.getline(name,20);

第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数。如果这个参数为20,则函数最多读取19个字符,余下的空间用于存储自动在结尾处添加的空字符。getline()成员函数在读取指定数目的字符或遇到换行符时停止读取。

同样用上节中的例子:

该程序现在可以读取完整的姓名和用户喜欢的甜点了。getline()函数每次读取一行,它通过换行符来确定行尾,但不保存换行符。相反,在存储字符串时,它用空字符来替换换行符。

b、面向行的输入:get()

istream类有另一个名为get()的成员函数,该函数有几种变体用法如下:

其中一种与getline()类似,参数相同,并且都读取到行尾,但是get()将换行符留在输入队列中,假如我们连续两次调用get():

cin.get(name,ArSize);

cin.get(dessert,Arsize);

由于第一次调用后,换行符留在输入队列中,因此第二次调用时看到的第一个字符就是换行符,get()认为已经到行尾了,而没有发现任何可读取的内容。所以在上面两条语句中,name中存储了一行字符串,而dessert中是空的。

get()还有另一种变体。即使用不带任何参数的cin.get()调用读取下一个字符(即使是换行符),因此可以用它来处理换行符,为读取下一行做好准备。上面语句可以换成如下:

cin.get(name,ArSize);

cin.get();

cin.get(dessert,Arsize);

这样第二个语句就把换行符读取了,然后再读第三条语句就没有影响了。

还有一种方式就是使用get()的方式将两个类成员函数拼接起来(合并),如下所示:

cin,get(name, ArSize).get();

同样,getline()也可以这么用:

cin.getline(name1,ArSize).getline(name2,ArSize);

还是上面的例子,可以这么表达:

从上面几个例子的对比我们可以总结一下,输入一行完整的字符串的方法有这么几种:使用cin.getline(name,20)或者使用cin.get(name,20).get()。那么我们应该选择使用哪一种呢?

当然,都可以,但是使用get()输入更仔细。例如,假如使用get()将一行读入数组中,如何知道停止读取的原因是由于已经读取了整行,而不是由于数组已经填满了呢?查看下一个字符,如果是换行符,说明已读取了整行,否则,说明该行中还有其他输入。总之,getline()使用起来更简单一些,但get()使得检查错误更简单些。

④混合输入字符串和数字

混合输入数字和面向行的字符串会导致跟上面同样的问题。

用户根本没有输入地址的机会,问题在于当cin读取年份之后,将回车键留在了输入队列中,后面的getline()看到换行符认为是一个空行,并将这个空字符串给了address数组。解决的方法也就是上面介绍的几种:

cin>>year;

cin.get();

或者(cin>>year).get();  或者 (cin>>year).get(ch);

C++程序常使用指针(而不是数组)来处理字符串,以后会讲到。

(2)string类简介

ISO/ANSI C++98标准通过添加string类扩展了C++库,因此现在可以用string类型的变量而不是字符串数组来存储字符串。string类使用起来比数组简单,同时提供了将字符串作为一种数据类型的表示方法。

要使用string类,必须在程序中包含头文件string。string类位于名称空间std中,因此需要使用using namespace std;指令。string类定义隐藏了字符串的数组性质,使得处理字符串像处理普通变量那样简单。

看下面的例子:

下面是运行结果:

从上面这个例子中可以看到,string对象和使用字符数组有很多相同之处:

可以使用C风格字符串来初始化string对象;

可以使用cin来将键盘输入存储到string对象;

可以使用cout来显示string对象;

可以使用数组表示法来访问存储在string对象中的字符(即可以将string对象看作一个数组,可以访问其中的某一个元素)。

字符串数组和string对象的主要区别是:可以将string对象声明为简单变量,而不是数组:

string str1;     string  str2 = "panther";

类设计让程序能够自动处理string的大小。例如,str1的声明创建一个长度为0的string对象,但是将输入读取到str1中时,将自动调整str1的长度;这使得与数组相比string对象更方便,也更安全。

①赋值、拼接和附加

string类的操作比使用数组时更简单。例如,不能将一个数组赋给另一个数组,但可以将一个string对象赋给另一个string对象。例如: string str1;  string str2 ="pantner";   str1 = str2;

string类简化了字符串的合并操作。可以使用运算符+将两个string对象合并起来,还可以使用运算符+=将字符串附加到string对象末尾。如 string str3;   str3 = str1+str2;  str1+=str2;

处理string对象的语法通常比使用C字符串函数简单,尤其是执行较为复杂的操作时。例如:

str3 = str1 + str2;

使用C-风格字符串时,需要使用的函数如下:strcpy(charr3,charr1);     strcat(charr3, charr2);

另外,使用字符数组时总是存在目标数组过小而无法存储指定信息的危险,如下例:char site[10] = "house";

strcat(site, " of pancakes"); 函数strcat()试图将全部12个字符复制到数组site中,这将覆盖相邻的内存。这可能道枝程序终止或程序数据被破坏。而string类具有自动调整大小的功能,从而避免这种问题的发生。

确定字符串字数的方法也有区别:int len = str1.size(); int len2 = strlen(charr1);

②string类的I/O

可以使用cin或cout来输入存储到string对象或输出,其句法与处理C-风格字符串相同。但是每次读取一行而不是一个单词。

时间: 2024-10-14 15:33:03

C++学习笔记之四 复合类型1的相关文章

Go语言学习笔记(4)复合类型

  Go语言的复合类型,包括数组.切片和映射等. 值.指针和引用类型 通常情况下Go语言中的变量持有相应的值.也就是说,我们可以将一个变量想象成它所持有的值来使用.其中有些例外,通道.函数.方法.映射.切片是 引用变量,它们持有的都是引用,也即保存指针的变量.值在传递给函数或者方法的时候会被复制一次,对于布尔类型和数值类型来说这非常廉价,但是对于大型变 量代价却非常大.而且复制传参的方式,修改值只是修改了副本,这能保证原始变量不被修改,但也一定程度上增加了修改原始值的麻烦.幸好在Go语言中有指

MyBatis association的两种形式——MyBatis学习笔记之四

一.嵌套的resultMap 这 种方法本质上就是上篇博文介绍的方法,只是把教师实体映射从association元素中提取出来,用一个resultMap元素表示.然后 association元素再引用这个resultMap元素.修改上篇博文示例的StudentMapper.xml如下: <?xml version="1.0" encoding="utf8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org

马哥Linux学习笔记之四——DNS

1.BIND:Berkeley Internet Name Domain DNS:Domian Name Service 域名解析 2. Http 3.PAM 插入式认证模块 4.SMTP/POP3/IMAP4:Mail Server 5.域名 www.baidu.com这是一个主机名(FQDN,Full Qualified Domain Name,完全限定域名),com是一个域名,baidu.com也是一个域名,域名是好多主机的集合. 域名解析起后面有一个数据库,解析就是一个查询的过程.域名解

Swift学习笔记(二)参数类型

关于参数类型,在以前的编程过程中,很多时间都忽视了形参与实参的区别.通过这两天的学习,算是捡回了漏掉的知识. 在swift中,参数有形参和实参之分,形参即只能在函数内部调用的参数,默认是不能修改的,如果想要修改就需要在参数前添加var声明. 但这样的声明过后,仍旧不会改变实参的值,这样就要用到inout了,传递给inout的参数类型必须是var类型的,不能是let类型或者字面类型,(字面类型是在swift中常提的一个术语,个人认为就是赋值语句,也不能修改)而且在传递过程中,要用传值符号"&

C# in Depth Third Edition 学习笔记-- 可空类型

1. 没有值怎么办? 客户下了一份订单,有订货日期,但尚未发货,但没有发货日期,怎么表述发货日期? C#2以后使用可空类型. 2. 为什么值类型的变量不能是null? 引用类型变量,其值是一个引用,即一个非空引用值提供了访问一个对象 的途径,对于null来说,作为一个特殊值,意味着不引用任何对象.所有引用的默认值都为null,内存中表示全零. 值类型变量,其值是它本身的真实数据.null不是有效的值类型的值. 3. C#1 解决不存在可空值类型 魔值:DateTime,牺牲公元元年中的某个值Da

MySQL学习笔记之三 表类型

你能用的数据库引擎取决于MySQL在安装时候是如何被编译的.要添加一个新的引擎,就必须编译MySQL.仅仅为了添加一个特性而编译应用程序的想法对于Windows的开发人员来说可能有点小题大做,得不偿失,但是在Unix的世界里,这已经成为了标准.在缺省的情况下,MySQL支持三个引擎:ISAM.MyISAM和HEAP.另外两种类型InnoDB和Berkley(BDB),也常常可以使用. ISAM ISAM是一个定义明确且历经时间考验的数据表格管理方法,它在设计之初就考虑到数据库被查询的次数远远大于

Citrix XenMobile学习笔记之四:MIM移动信息管理(Mobility Information Management)

产品简介: Citrix MIM移动信息管理由Citrix ShareFile这一产品来集成.Citrix ShareFile是一种企业"数据跟随(Follow-me-data)"解决方案,使IT部门可交付可靠的数据共享和同步服务,满足用户的移动性和协作需求,以及企业的数据安全需求.ShareFile使"数据跟随(Follow-me data)"服务成了每个用户日常生活中无缝而直观的组成部分,可帮助当前高度机动化.随时随地通过任何设备办公的员工队伍确保最高的生产率.

初探swift语言的学习笔记(可选类型?和隐式可选类型!)

可选类型.隐式可选类型 其次swift还引入一个较有趣的初始值设置语法使用"?"操作符及"!"号操作符 如:"var optionalString: String? = "Hello" optionalString == nil var optionalName: String? = "John Appleseed" var greeting = "Hello!" if let name = op

linux网络编程学习笔记之四 -----多线程并发服务端

相对于使用进程实现并发,用线程的实现更加轻量.每个线程都是独立的逻辑流.线程是CPU上独立调度运行的最小单位,而进程是资源分配的单位.当然这是在微内核的操作系统上说的,简言之这种操作系统的内核是只提供最基本的OS服务,更多参看点击打开链接 每个线程有它自己的线程上下文,包括一个唯一的线程ID(linux上实现为unsigned long),栈,栈指针,程序计数器.通用目的寄存器和条件码,还有自己的信号掩码和优先级.同一个进程里的线程共享这个进程的整个虚拟地址空间,包括可执行的程序文本.程序的全局