c++(3)

c++(3)

数组

能够存储多个同类型的值,在内存中连续排布。

数组声明:存储在每个元素中的值的类型;数组名;元素数

typeName arrayName[arraySize]
such as short months[12]

编译器不会检查使用的下标是否有效,但是程序运行后,可能会引发问题。

只有在定义数组时才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组

int cards[4] = {3,6,8,10}; //√
int hand[4];
hand[4] = {5,6,7,9}; //×
hand = cards; //×

如果只对数组的一部分进行初始化,则编译器将把其他元素设置为0

字符串

c++处理字符串的方式有两种,c风格字符串+基于string类库的方法

c风格字符串
字符串存储在char类型数组

以空字符(‘\0’)结尾–>字符串和字符数组的区别

char dog[8] = {'a','b','c'}; //not a string
char cat[8] = {'a','b','c','\0'}; //a string

用引号括起的字符串隐式地包括结尾的空字符,因此不用显式地包括它。应确保数组足够大,能够存储字符串中所有字符-包括空字符。

char bird[11] = "Mr.Cheeps"; // a string

单引号’A’是字符常量,双引号"A"是字符串

cin使用空白字符(空格、换行符和制表符)来确定字符串的结束位置,这意味着cin在获取字符数组输入时只读取一个单词。

char name[20];
cin >> name;
//输入 Alistair Dreeb时,只会将Alistair存入name数组
采用面向行的字符串读取方法

istream中的类提供了面向行的类成员函数:getline()和get()。这两个函数都读取一行输入,直到到达换行符。然而,随后getline()将丢弃换行符,而get()将换行符保留在输入序列中。

getline()使用起来更简单一些,但get()使得检查错误更简单些。

  1. getline()

    读取整行,通过回车键输入的换行符来确定输入结尾。要调用这种方法,可以使用cin.getline()。该函数有两个参数:第一个参数是用来存储输入行的数组的名称;第二个参数是要读取的字符数(包括一个空字符’\0’)。getline()成员函数在读取指定书目的字符或遇到换行符时停止读取。

    cin.getline(name,20);
    
  2. get()

    get()函数不再抛弃换行符,而是将其留在输入队列中。

    cin.get(name,20);
    cin.get(dessert,20);
    //如果输入Alistair Dreeb,程序会出现问题,这是因为第一次调用之后,换行符将留在输入队列中,因此第二次调用时看到的第一个字符就是换行符,get()便会认为已经到达行尾,而没有发现任何可读取的内容。
    

    get()使用不带任何参数的cin.get()调用可读取下一个字符(即使是换行符),因此可以使用它来处理换行符,为读取下一行输入做好准备。

    cin.get(name,20);
    cin.get();
    cin.get(dessert,20);
    等价于
    cin.get(name,20).get();
    
混合输入数字和面向行的字符串会导致问题
#include<iostream>
int main()
{
	using namespace std;
    cout >> "What year was your house built?\n";
    int year;
    cin >> year;
    cont << "What is its straat address?\n";
    char address[80];
    cin.getline(address, 80);
    cout << "Year built : "<< year << endl;
    cout << "Address: " << address <<endl;
    cout << "Done!\n";
    return 0;
}

输出

What year was your house built?
1966
What is its straat address?
Year built : 1966
Address: 
Done!

问题:cin读取年份时,将回车键生成的换行符留在了输入队列中。后面的cin.getline()看到换行符后,将认为是一个空行,并将一个空字符串赋给address数组。解决方法是在读取address之前先读取并丢弃换行符。

cin >> year;
cin.get();
string 类

string类型的变量(不是字符数组)来存储字符串

要使用string,必须在程序中包含头文件string。string类位于名称空间std中,因此要提供一条using编 译指令或者使用std::string来引用。

string str1;
string str2 = "panda";

类设计让程序能够自动处理string的大小。

赋值、拼接和附加

可以将一个string对象赋给另一个string对象

string str1;
string str2 = "panda";
str1 = str2;

string类可以使用运算符+将两个string对象合并起来,还可以使用运算符+=将字符串附加到string对象的末尾。

string str3;
str3 = str1 +str2;
str1 += str2;

对比字符数组操作

strcpy(charr3, charr1);// s2=s1
strcat(charr3, charr2);// s3=s3+s2

strlen()和size()功能基本相同

int len1 = str1.size();
int len2 = strlen(charr1);
string类I/O

当读取一个单词时,可以使用类似于c风格字符串的处理方式,用cin << 来将输入存储到string对象;使用cout >> 来显示string对象。但每次读取一行而不是一个单词时,使用的语法不同。

getline(cin, str);

结构体

结构体是一种比数组更灵活的数据格式,因为同一个结构体可以存储多种类型的数据。

struct name
{
	char name[20];
	float volume;
	double price;
};
struct name product_a;//keyword struct  required in c
name product_b;//keyword struct not required in c++

使用成员运算符.来访问结构体成员,product_a.name

根据需要,可以将结构体定义在全局或者函数内部。

结构体可以将string类作为成员

#include<string>
struct inflatable
{
	std::string name;
	float volume;
	double price;
}

可以使用赋值运算符(=)将结构体赋给另外一个同类型的结构体,这样结构中的每一个成员都被设置为另一个结构中相应成员的值,即使成员是数组,这种赋值称为成员赋值。

struct inflatable
{
	char name[20];
	float volume;
	double price;
}
inflatable bouquet=
{
    "sunflowers",
    0.20,
    12.49
};
inflatable choice;
choice = bouquet;

结构数组

创建元素为结构体的数组

inflatable gifts[100];

cin >> gifts[0].volume;
cout << gifts[99].price <<endl;

共用体

共用体数一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型。(结构体是和的关系,共用体是或的关系)。

共用体每次只能存储一个值(各种值共用一块内存),其长度为最大成员的长度。

共用体常用于节省内存。

union one4all
{
	int int_val;
	long long_val;
	double double_val;
}
one4all pail;
pail.int_val = 15;
pail.double_val = 1.38;

枚举

c++的enum工具提供了另一种创建符号常量的方式,这种方式可以代替const,还允许定义新类型。

enum spectrum {red, orange, yellow, green, blue, violet, indigo, ultraviolet};
  • red、orange、yellow等作为符号常量,对应整数值0-7

  • 枚举只定义了赋值运算符,没有定义算术运算

  • 枚举可以被提升为int类型,但int类型不能自动转换为枚举类型

  • 如果int值有效,则可以通过强制类型转换,将它赋给枚举类型

    band = spectrum(3);
    

枚举更常用来被定义相关的符号常量(red = 0, orange = 1, yellow = 3…)

每个枚举都有取值范围,通过强制类型转换,可以将取值范围中的任何整数值赋给枚举变量,机制这个值不是枚举值。

enum bits {one = 1, two = 2, four = 4, eight = 8};
bits myflag;
myflag = bits(6)

取值范围的定义如下:首先找到上限,需要知道枚举量的最大值,找到大于这个最大值的、最小的2的幂,将它减去1,得到的便是取值范围的上限。要计算下限,需要知道枚举量的最小值,如果它不小于0,则取值范围的下限为0,;否则与寻找上限方式相同的方式,但加上负号。

指针与自由存储空间

指针是一个变量,存储的是值的地址,而不是值本身。查看常规变量的地址,使用地址运算符&。

int updates = 6;
int * p_updates;
p_updates = &updates;
//express values two ways
cout << "Values:updates = " << updates;
cout << ", *p_updates = " << *p_updates <<endl;
使用new来分配内存

在c语言中,可以使用库函数malloc()来分配内存;在c++中仍可以这样做,但c++还有更好的方法——new运算符 。

typeName * pointer_name = new typeName

int * pn =new int;

new分配的内存块通常与常规变量分配的内存块不同。常规变量存储在栈中,而new从北称为堆或自由存储区的内存区域分配内存。

使用delete来释放内存

delete运算符使得在使用完内存后,能够将其归还给内存池。

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

不要尝试释放已经释放的内存块

new和delete要成对使用

用new创建动态数组

如果通过声明来创建数组,则在程序被编译时将为它分配内存空间,不管程序最终是否使用数组,数组都在那里,它占用了内存。在编译时给数组分配内存被称为静态联编,意味着数组是在编译时加入到程序中的。

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

使用静态联编时,必须在编写程序时指定数组的长度;使用动态联编时,程序将在运行时确定数组的长度。

type_name * poiinter_name = new type_name [num_elements]

new运算符返回第一个元素的地址。

int * psome = new int [10];
...
delete [] psome;

访问动态数组元素:只要把指针当做数组名使用即可。

#include<iostream>
int main()
{
	using namespace std;
	double * p3 = new double [3];
	p3[0] = 0.2;
	p3[1] = 0.5;
	p3[2] = 0.8;
    cout << "p3[1] is " << p3[1] << endl;
	p3 = p3 + 1;
    cout << "Now p3[0] is " << p3[0] << endl;
    delete [] p3;
    return 0;
}

指针、数组和指针算术

指针与数组名:指针是变量,其值可以被修改,而数组名是常量。对数组应用sizeof运算符得到的是数组的长度(这种情况下,c++不会将数组名解释为地址),而对指针应用sizeof得到的是指针的长度,即使指针指向的是一个数组。

在多数情况下,数组名等于数组第一个元素的地址(sizeof除外),而对数组名应用地址运算符时,得到的是整个数组的地址。

short tell [10];
cout << tell << endl;
cout << &tell << endl;

从数字上说,这两个地址相同;但是从概念上说,&tell[0](即tell)是一个2字节内存块的地址,,而&tell时一个20字节内存块的地址。因此,表达式tell+1将地址值加2,而表达式&tell+1将地址加20。(步长改变)

数组指针和指针数组:

//指针数组,
// pos先与[20]结合,pos是一个数组,每一个元素是short*类型(指向short类型值的指针)
short *pos[20];

//数组指针
// pos先与*结合,pos是一个指针,指向一个数组,数组有20个元素,每一个元素时short类型
short (*pos)[20];
指针和字符串

如果给cout提供一个字符的地址,则它将从该字符开始打印,直到遇到空字符为止。如果想要打印地址,进行强制类型转换。

char animals[20] = "tigger";
cout << animals << "at" << (int *)animals << endl;

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

常量指针(const):指针的指向可以修改,但是指针指向的值不可以改

使用new创建动态结构体

在程序运行时(而非编译时)为结构体分配所需的空间

inflatable * ps = new inflatable //inflatable 是自定义的结构体

访问结构体成员的两种方式:

  1. 箭头成员运算符(->):如果ps指向一个inflatable结构体,则ps->price是被指向的结构体的price成员

    struct things
    {
    	int good;
    	int bad;
    };
    things grubnose = {3,453};
    things *pt = &grubnose;
    cout << grubnose.good << " and " << grubnose.bad <<endl;
    cout << pt->good << " and " << pt->bad <<endl;
    
  2. 如果ps是指向结构体的指针,则*ps就是被指向的值(结构体本身),( *ps).price是该结构的price成员。

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

自动存储

在函数内部定义的常规变量使用自动存储空间,被称为自动变量,这意味着它们在所属的函数被调用时自动产生,在该函数结束时消亡。

自动变量是一个局部变量,其作用域为包含它的代码块。代码块是被包含在花括号中的一段代码。

自动变量通常存储在栈中。

静态存储

静态存储是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字static。

动态存储

new和delete运算符提供了一种比自动变量和静态变量更灵活的方法,它们管理了一个内存池,在c++中被称为堆。数据的生命周期不完全受程序或函数的生存时间控制。

数组的替代品

模板类vector

动态数组

是使用new创建动态数组的替代品

要点

  • 要使用vector对象,必须包含头文件vector
  • vector包含在名称空间std中,因此可使用using编译指令、using声明或std::vector
  • 模板使用不同的语法来指出它存储的数据类型
  • vector类使用不同的语法来指定元素数

vector <typeName> vt(n_elem)

#include<vector>
...
using namespace std
vector <int> vi;
int n;
cin >> n;
vector <double> vd(n);
模板类array

功能强大但效率较低

位于名称空间std中,要创建array对象,需要包含头文件array

array对象的长度是固定的(和数组一样),也使用栈,相比于数组,其更方便更安全。

arryy<typeName,n_elem> arr

#include<array>
...
using namespace std;
array<int, 5> ai;
array<double, 4> ad = {1.2, 3.4, 5.6, 7.3};

可以将一个array对象赋给另一个array对象

c++不检查数组的越界错误,可以使用vector和array的成员函数at()在程序运行期间捕获非法索引,而程序默认将中断,这种额外检查的代价是运行时间更长。

vector <double> a2(4);
a2.at(1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值