C++入门第四章:复合类型

1 数组

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

使用数组前,首先要声明。声明包括三个方面:

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

声明的通用风格如下:

typename arrayname[arrysize];

注;arrysize指定元素数目,必须是整型常量,不能是变量。

数组的很多用途均基于这样一个事实:可以单独访问数组元素。方法是使用下表或索引对元素进行编号。C++数组从0开始编号,并使用带索引的方括号表示法来指定数组元素。

注:编译器不会检查使用的下表是否有效,所以确保有效的下标值非常重要。

数组的初始化规则

只有在定义时才能使用,以后就不能用了,也不能将一个数组赋值给另一个数组。

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

初始化时提供的值可以少于元素数目。如果只对数组的一部分进行初始化,则编译器将其他元素设置为0。

如果初始化数组的时候方括号内为空,编译器将计算元素的个数。如:

short a[]={1,2,3,4};

编译器使a数组包含4个元素。

注:让编译器去计算数组大小是很糟糕的事情。

C++11数组初始化方法

首先,初始化时能够省略等于号。

int a[2]{1,2};

其次,不必在大括号内包含任何东西,这将把所有元素全部置0。

int b[10]={};

第三,列表初始化禁止缩窄。

long c={1.0,2.0,4} //不允许

2 字符串

字符串是存储在内存的连续字节中的一系列字符。

C++使用两种方法处理字符串。一种是C风格字符串,另种是基于string类的方法。

C-风格字符串以空字符作为字符串的结尾。

例:char c1={'a','b','c','d','e','f'}; //不是字符串

char c2={'a','b','c','d','e','\0'}; //字符串

cout打印字符串碰到\0后停止。

C++中另一种初始化字符串的方法:双引号。

char c3[11]="C++ language.";

char c4[]="C++ language.";

在这时,让编译器自己计算字符串长度显得更安全。

注意,字符串常量(双引号)和字符常量(单引号)不能够互换。

字符串的返回值是首字母的地址。

拼接字符串:cout<<"a" "b";

字符串的输入输出

cin使用空白(空格、制表符和换行符)来确定字符串的结束位置。这意味着cin每次仅能读取一个单词。

每次读取一行字符串输入

  • 面向行的输入:getline()

getline()函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。使用cin.getline()来调用此种方法。函数有两个参数:第一个参数用来存储输入行的数组名称,第二个参数为要读取的字符数。最多读取数为该数字减1(最后一个存储\0)。此函数在读取指定字符或者遇到换行符时自动停止读取。

例:cin.getline(name,20),最多包涵19个字符。

此函数自动忽略换行符。

  • 面向行的输入:get()

基本用法与getline()相同,但是此函数不丢弃换行符。

例:cin.get(name,20)

cin.get()可自动读取下一字符。

另:类成员拼接:

cin.get(name1,20).get();

或:cin.getline(name1,20).getline(name2.20);

空行输入

get()遇到空行时会设置失效位使输入阻断。

使用cin.clear();可删除失效位,恢复输入。

和溢出会产生问题。

字母数字混合输入的问题:

cin>>year;

cin.get();

3 string类简介

C++98标准通过添加string类扩展了C++库,因此现在可以使用string类型的变量(C++中的对象)而不是字符数组来存储字符串。

要使用string类,必须使用名称空间std并且包含头文件string。

string的方式:

  • 可以使用C语言风格来初始化string变量。
  • 可以使用cin将键盘输入存储到string对象中。
  • 可以使用cout来显示string对象。
  • 可以使用数组表达式法来访问存储在string中的字符。

getline(cin,str);

string类将字符串声明为一个简单变量。

程序能够自动处理string的大小。

string类简化了字符串的合并操作。使用"+"可以实现字符串合并,使用"+="可实现在一字符串后添加字符串。

str1.size()返回字符串str1的长度,size()为一个类方法。

其他形式的字符串字面值

C++还有类型wchar_t,C++11新增了类型char16_t、char32_t。C++使用前缀L、u和U表示。

例:wchar_t a[]=L"abcd";

char16_t a[]=u"abcd";

char32_t a[]=U"abcd";

C++新增了一种字符串类型:原始字符串(raw)。在原始字符串中,字符表示的就是自己。例如序列\n表示的就是\和n而不是换行符。

例:cout<<R"ab "c",\n"<<endl; 显示为:ab "c",\n

cout<<R"+*(abc(abc)"a"\n.)*+"; 显示为:abc(abc)"a"\n.

即:使用R"";可在其中加\和双引号。使用R"+*()*+"可显示加\、双引号和括号。

  • 结构简介

结构是一种比数组更灵活的数据格式,统一结构可以存储不同类型的数据。结构是C++OOP编程的基石。

结构定义:

struct structname

{

datatype1 name;

……..

}

结构声明与使用:

C语言形式:struct structname a

C++形式:structname a

C++声明结构变量时可以省略struct

变量声明在函数内,结构声明在函数外。

变量初始化的方式:

例:已定义了一个结构变量student(包含姓名、学号和姓名)。则可通过如下方式初始化:

student a={"Tom",123456,20}或

student a {"Tom",123456,20}

即由逗号分隔值列表,并将这些值用花括号括起。在程序中,每个值可以个占一行,也可以全部放在同一行。赋值符号可以省略。

初始化时大括号内可以为空,此时全置为0,如果有未初始化的成员,置为0。

最后,不允许缩窄变换。

其他结构属性

可以将结构作为参数传递给函数,也可以让函数返回一个结构。可将一个结构赋给另一个结构。

结构数组

结构体变量也可组成数组,例:(struct) student a[100];引用方式:a[1].name。

初始化方式:

student a[2]=

{

{"Tom",20110601,20},

{"Jack",20110602,20}

};

当中都要使用逗号分隔。

  • 共用体

共用体是一种数据格式,可以存储不同的数据类型,但只能同时存储其中的一种类型。

例:union student

{ char name[20];

int number;

int age;

};

每个时刻共用体只能存储一个值。

匿名共用体:

例:struct student

{ char name[20];

union

{ int id1;

int id2;

};

}a;

可使用a.id1引用结构体中的id1。

  • 枚举

enum工具提供了另一种创建符号常量的方式。使用方法与结构体类似。

例:enum day {Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};其中,Sunday=0,Monday=1,以此类推。

上面定义的Sunday等是符号常量,又被称为枚举量。

枚举量有一些特殊属性:

  • 在不进行强制类型转换的时候,只能将定义枚举时使用的枚举量赋给这种枚举的变量。例:

enum day;

day a;

a=Sunday; //正确

a=0; //不正确,非枚举量

  • 可通过强制类型转换进行赋值。例:

a=day(1);但是a=day(10)是错误的。

若只是使用枚举量创建常量,可直接使用以下语句定义:enum {a,b,c,d};省略枚举类型的名称。

设置枚举量的值

enum bit{one=1,two=2,four=4};

其中,指定的值必须为整数。

enum num{a=1,b,c=9,d}; b=2,d=10;

枚举值的取值范围

取值范围的定义:首先找出枚举值中最大的数。在2的幂中,找到比这个数大的最小值,然后减1即为上限。(如:枚举中的最大值为101,则上限为2^7-1=127)。如果最小值不小于零,下限为0,否则,与上限同理。

  • 指针和自由存储空间

指针是一个变量,其存储的是值的地址,而不是值本身。

对于常规变量,应用地址运算符(&),就可以获得它的位置。比如home为一变量,&home,就是变量的地址。

使用常规变量时,值是指定的量,地址为派生量。而指针将地址视为指定的量,而将值视为派生量。指针名表示的是地址,*运算符被称为间接值或解除引用运算符,将其运用于指针,可以得到该地址处存储的值。

一般计算机使用16进制表述指针,也有十进制的。

指针的声明

例:int *p_updates;其中,p_updates为地址。

传统C语言初始化方式:int *p;

C++方式:int* p;表示int* 为一种指向int类型的指针。

甚至也可以这样:int*p;

注:对每个指针变量名,都必须使用*,即int* p1,p2;其中p2为一个int型变量。

指针的危险

在C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存。为数据提供空间是一个独立的步骤。

例:long *a;*a=233333;

上述代码并没有将地址赋给a,233333将放在哪里呢?我们不知道,a指向的地方并不是233333的地址。所以一定要在指针使用*之前初始化它。

指针和数字

指针不是整型,但是计算机通常把指针当做整型变量来存储。指针不能进行乘的运算,不能将整数赋给指针。如:

int *p;p=0xB8000000;

但是可以通过强制类型转换赋值。即:

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

使用new来分配内存

int *pnew=new int;

new运算符实现了在运行阶段为int值分配未命名的存储空间,并通过指针来访问这个值。

如果使用的是:int a;int *pt=&a;则可使用a来访问该值,而使用new只能够使用指针访问该值。

我们称pnew指向的是一个"数据对象"。处理数据对象的指针方法可能不一定好用,但是这在管理内存方面有更大的控制权。

为一个数据对象(基本类型或者结构)获得并指定分配内存的方法:

typename *pointer_name=new typeName;

使用举例:

int *pn=new int;

*pn=1001;

使用delete释放内存

使用delete释放new分配的内存。

例:int *pn=new int;

delete pn;

但是这并不是意味着删除指针pn本身,删除的只是pn指向的内存块而已。

new和delete应该成对使用,否则会出现内存泄露的情况。

使用new来创建动态数组

使用声明来创建一个数组,在编译时给它分配内存空间,属于静态联编,使用new来创建数组,在运行时分配空间,属于动态联编。

例:int *pn=new int [10]; //创建动态数组

delete [] pn; //删除动态数组

上面的pn指向数组中的第一个值。

即:

type_name *pointor_name=new type_name [num_elem];

其中,num_elem可以是整型常量或者变量。

使用动态数组的方法:

以上面的pn为例,可使用pn[0]……pn[9]访问数组元素。也可使用*(pn+1)进行访问。

  • 指针、数组和指针算数

指针和数组基本等价的原因在于指针算术和C++内部处理的方式。C++将数组名作为指针,且为常量。

将指针变量加1后,增加的值等于指向类型占用的字节数。

注:数组名代表数组第一个值的地址,&数组名代表整个数组的地址。

指针和字符串

例:char a[20]="name";

我们可以认为a是字符串第一字符'n'的地址。如果使用cout输出的话,从'n'开始输出,知道碰到\0结束。

注:在cout和多数C++表达式中,char 数组名、char 指针以及使用引号括起来的字符串常量都被解释为字符串第一个字符的地址。

字符串的字面值是常量。

注:在将字符串读入程序时,应使用已分配的内存地址。该地址可以是数组名、也可以是使用new初始化过的指针。但不可使用字符串常量和为被初始化过的指针来接受输入。

一般来说,如果给cout提供一个指针,他将打印指针。但如果指针的类型为char *,cout将显示字符串,如果要显示地址,必须使用强制类型转换,如:(int *)字符指针。

例:char a[20]="an animal";

char *ps=new char[strlen(a)+1];

strcpy(ps,a);

注:上面的程序中不能使用ps=a;否则ps分配的内存将无法使用。

另:strcpy(a,b);//a:目标地址,b:源地址

如果b的长度比a定义的长,则会继续复制。

strncpy(a,b,n);//a:目标地址,b:源地址,n:长度

使用new创建动态结构

首先定义一个结构

struct student

{

char name[10];

int num;

int age;

};

student *ps=new student;

此时,要使用"->"运算符引用结构中的元素。(也可以使用(*ps).num引用)。

自动存储、静态存储和动态存储

根据分配内存的方式,C++有三种管理数据内存的方式:自动存储、静态存储和动态存储。C++11新增了一种:线程存储。

  1. 自动存储

函数内部定义的常规变量使用自动存储空间,被称为自动变量。函数调用时产生,函数结束时消亡。

自动变量为一局部变量,作用域为代码块(花括号中的一段代码)。

自动变量存储在栈中。

  1. 静态存储

整个程序执行期间都存在的变量,使用static声明。

3,动态存储

new和delete提供的方式。

  • 类型组合

本章介绍的数据类型可以组合

struct student

{ char name[20];

int num;

int age;

}

现在声明一个变量:

student Tom;

可使用Tom.age访问结构中的成员。

若 student * ptom=&Tom;

则使用ptom->age访问。

若声明一个数组:student s[3];

s[0].age 访问成员

(s+1)->age 访问其成员

同时可创建指针数组:

const student *pp[3]={&s[1],&s[2],&s[3]};

访问成员方法:

std::cout<<pp[1]->age<<std::endl;

可创建指向指针的指针:

const student **ppp=pp;

C++11中也可写成:const student ppp=pp;

  • 数组的替代品

  • 模板类vector

模板类vector类似于string类,也是一种动态数组。可在运行阶段设置vector的长度,可在末尾附加新数据。

  • 使用模板应包含头文件vector
  • vector包含在名称空间std中
  • 模板使用不同的语法来指出存储的数据类型
  • vector使用不同的语法来指定元素数

例:#include<vector>

using namespace std;

vector<int> vi;

int n;

cin>>n;

vector<double> vd(n);

声明vector对象的方法:

vector<typename> vt(n_elem);

其中n_elem可以是整型常量或者变量,省略则默认为0。

2 模板类array

C++11中提供的新类型,固定长度的数组,但是比普通数组更方便和安全。

例:

#include<array>

using namespace std;

array<int,5> ai;

array<double,4> ad={1.1,1.2,1.3,1.4,1.5};

声明方式:

array<typename,n_elem> arr;

其中n_elem必须是整型常量

总结

array和数组存放在栈中,vector存储在自由存储区或者堆中。

array对象可以整个赋值。

例:

array<double,4> a1={1.1,1.2,1.3,1.4,1.5};

array<double,4> a2;

a2=a1;是合法的。

a1[-2]=199;是合法的。

即*(a1-2)=199;

但是会产生意想不到的错误。

vector和array避免此类错误的方法:

a1.at(1);

如果它捕获非法索引,则中断程序。

总结

数组、结构和指针是C++中的3种复合类型。

数组可以在一个数据对象中存储多个同类型的数据。通过索引和下标访问。

结构可将多个不同类型的值存放在同一数据对象中。使用成员关系运算符(.)来访问其中的成员。

共用体只能存储一个值。

指针使用来存储地址的变量。new运算符允许程序运行时为数据对象提供内存,返回获得的内存地址。

C++98新增的标准模板库(STL)提供了模板类vector。

C++ Primer Plus学习:第四章的更多相关文章

  1. C Primer Plus 学习 第四章

    字符串与格式化输入/输出 函数 strlen() 关键字 const 利用#define 和 const创建符号常量 #include <stdio.h> #include <str ...

  2. Java基础知识二次学习--第四章 异常

    第四章 异常处理   时间:2017年4月26日11:16:39~2017年4月26日11:28:58 章节:04章_01节 04章_02节 视频长度:20:46+01:16 内容:异常的概念 心得: ...

  3. C#高级编程 (第六版) 学习 第四章:继承

    第四章 继承 1,继承的类型 实现继承: 一个类派生于一个基类型,拥有该基类型所有成员字段和函数. 接口继承 一个类型只继承了函数的签名,没有继承任何实现代码.   2,实现继承 class MyDe ...

  4. 【C++】《C++ Primer 》第十四章

    第十四章 重载运算与类型转换 一.基本概念 重载运算符是具有特殊名字的函数:由关键字operator和其后要定义的运算符号共同组成.也包含返回类型.参数列表以及函数体. 当一个重载的运算符是成员函数时 ...

  5. 深度学习框架PyTorch一书的学习-第四章-神经网络工具箱nn

    参考https://github.com/chenyuntc/pytorch-book/tree/v1.0 希望大家直接到上面的网址去查看代码,下面是本人的笔记 本章介绍的nn模块是构建与autogr ...

  6. C++ Primer Plus学习:第二章

    C++入门第二章:开始学习C++ 进入C++ 首先,以下是一个C++程序: //myfirst.cpp 显示一行文字 #include<iostream> //预处理器编译指令 int m ...

  7. java并发学习--第四章 JDK提供的线程原子性操作工具类

    在了解JDK提供的线程原子性操作工具类之前,我们应该先知道什么是原子性:在多线程并发的条件下,对于变量的操作是线程安全的,不会受到其他线程的干扰.接下来我们就学习JDK中线程的原子性操作. 一.CAS ...

  8. Intel汇编语言程序设计学习-第四章 数据传送、寻址和算术运算-下

    4.3  和数据相关的操作符和伪指令 操作符和伪指令并非机器可执行的指令,相反,它们是由汇编器进行解释的.开发者可以使用一系列的MASM操作符或伪指令获取数据的地址以及大小等特征信息: OFFSET操 ...

  9. C语言学习第四章

    今天学习C语言循环结构,为什么要用循环呢?因为有时候我们对一堆的数字进行重复的处理的时候要重复的编写一些相同或者差不多的代码,让程序显得很臃肿,而且写着也麻烦,如果用循环来写的话能简化很多,出错的话也 ...

  10. Artech的MVC4框架学习——第四章Model元数据的解析

    总结: 第一Model元数据是针对 数据类型的一种表述信息. 第二Model元数据作用:控制数据类型本身及其成员,通过相应的特性,在view中 为绑定的数据(Model)实现模版化的html呈现. 第 ...

随机推荐

  1. springboot配置kafka生产者和消费者详解

    在原有pom.xml依赖下新添加一下kafka依赖ar包 <!--kafka--> <dependency> <groupId>org.springframewor ...

  2. 基于STM32的简易数码相册

    原理:在板子上插入SD卡,并使用FATFS文件系统来循环读取并显示SD卡内的指定目录内的所有BMP图片. 这是显示效果(能上传视频的话就能看到循环显示效果): 因为图片显示函数显示的是24位BMP图片 ...

  3. Zeta--S3 Linux抓取一帧YUV图像后使用硬件编码器编码成H.264

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <getopt.h&g ...

  4. ELKStack入门篇(一)之ELK部署和使用

    一.ELKStack简介 1.ELK介绍 中文指南:https://www.gitbook.com/book/chenryn/elk-stack-guide-cn/details ELK Stack包 ...

  5. 微信小程序:页面跳转时传递数据到另一个页面

    一.功能描述 页面跳转时,同时把当前页面的数据传递给跳转的目标页面,并在跳转后的目标页面进行展示 二.功能实现 1. 代码实现 test1页面 // pages/test1/test1.js Page ...

  6. idea里绝对不要直接复制文件到项目中的另一处

    否则那样会将使用被复制文件的那些地方 文件名会变成复制后的那个 而路径是原来的 所以会导致找不到文件 所以绝对不要直接复制文件或者包或者目录到项目中的另一处 需要时应该新建文件 把代码复制进去 这种事 ...

  7. 【RAC搭建报错】在RAC搭建到grid安装前的检查时,报错

    这种ip的报错,无非是检查防火墙,ip配置的原因 而我防火墙已关闭,ip也没配错 最后的原因是因为我172.16.1.41/42这两个IP选的虚拟机没有配置网段 [grid@rac01 grid]$ ...

  8. 记录阿里云ECS(Centos7.4)安装mysql 8.0.X服务

    #*.rpm介绍 大多数二进制rpm包都包含在名称中倒数第二个字段中编译rpm的体系结构..rpm软件包有那么几种 *.src.rpm 源程序包,要先通过编译才能安装 *.noarch.rpm 该包适 ...

  9. Charles的安装与破解

    Charles启动需要安装java环境,不知配置了jdk就可以,而是需要java环境,否则下载后点击启动会提示找不到suitable java 1 安装java环境 安装java环境是在https:/ ...

  10. JMeter:全面的乱码解决方案【转】

    本文是转自https://www.cnblogs.com/mawenqiangios/p/7918583.html 感谢分享者   中文乱码一直都是比较让人棘手的问题,我们在使用Jmeter的过程中, ...