重学C++笔记之(三)复合类型

1. 数组

数组(array)是一种数据格式,能够存储多个同类型的值,每个值都存储在一个独立的数组元素中,计算机在内存中依次存储数组的各个元素。它的声明应该指出以下三点。

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

1.1 定义

定义模板:

typeName arrayName[arraySize];

例如:

short mouths[12];

注意以下几点:

  • 其中arraySize不能是变量。
  • 数组元素通过方括号访问,例如mouths[0]
  • 数组从0开始编号,所以元素的最后一个索引比数组长度小1

1.2 数组的初始化

数组的初始化是在数组定义的时候使用的,一旦被定义,就不能再初始化。也能将一个数组赋给另一个数组(可以通过单个元素赋值)。例如:

int chards[4] = {
    
    1, 2, 3, 4};//可行
int hand[4];//可行
hand[4] = {
    
    1, 2, 3, 4};//不允许
hand = cards;//不允许

初始化数组时,有以下几种特殊初始化,比如初始化数组时,提供的值可以少于数组的元素数目。例如:

float hotelTips[5] = {
    
    5.0, 2.5};//只赋值给前两位,其它元素为0
int total[500] = {
    
    0}//所有元素赋值为0
short things[] = {
    
    1, 2, 3, 4}//初始化things数组大小为4

1.3 C++11数组初始化方法

之前就介绍过,C++11将使用大括号的初始化作为一种通用的初始化方式,可用于所有类型。

double earning[4]{
    
    1.1, 1.2, 1.3, 1.4};
int balance[10] = {
    
    };//可以包含空元素,所有元素都为0
int counts[10]{
    
    }//可以包含空元素,所有元素都为0

禁止缩窄转换,例如:

long plifs[] = {
    
    25, 92, 3.0};//不允许,浮点型转整型
char slifs[4]{
    
    'h', 'i',l12011,'\0'};//不允许,l122011超出char范围
char tlifs[4]{
    
    'h', 'i', l22,'\0'};//可以,虽然l12属于int值,但是在范围内。

**C++标准模板库(STL)提供了一种数组替代品,模板类vector,而C++11新增了模板类array。**这将在后续中介绍。

2. 字符串(C-风格)

C-风格字符串具有一种特殊的性质:以空字符结尾,空字符被写作\0,其ASCII码为0。

2.1 定义

  • 显示\0
char dog[4] = {
    
    'a', 'b', 'c', 'd'};//不是一个string
char cat[4] = {
    
    'a', 'b', 'c', '\0'};//string

上面的第一个char就是一个普通数组,不是一个string。如果用cout打印dog,那么就不止打印这4个字符,内存的剩下部分也会被打印,知道遇到“\0”截止。

  • 隐式\0

我们还可以使用字符串常量的方式对它进行初始化,这种方式可以让定义的数组比字符串长,因为我们只是以\0为结尾判断字符串长度。

char bird[11] = "mr. cheeps";
char fish[] = "bubbles";//让编译器自己计算长度

下面的情况将会报错,因为字符串表示的是一个地址,而且“S”表示的是’S’和’\0’组成的字符串。

char shirt_size = "S"//报错

2.2 拼接字符串常量

可以使用空白(空格、制表符和换行符)将分割的两个字符串常量自动拼接成一个。下面的输出等价:

#include<string>
#include<iostream>
using namespace std;
int main()
{
    
    
    cout<<"I am " "QLee\n";//空格
    cout<<"I am "   "QLee\n";//tab
    cout<<"I am "//换行
          "QLee\n";
}

2.3 cin一次读入一个单词

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

#include<iostream>
using namespace std;
int main()
{
    
    
    const int Arsize = 20;
    char name[Arsize];
    char dessert[Arsize];

    cout<<"Enter you name:\n";
    cin>>name;
    cout<<"Enter your favorite dessert:\n";
    cin>>dessert;
    cout<<dessert<<endl;
    cout<<name<<endl;
}

测试:
在这里插入图片描述
可以看到,程序只能我们输入了一次。

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

  • getline()

它是通过回车键判断输入结尾。比如

cin.getline(name, 20);//name:存储的数组,最多读取19个字符
//余下的空间自动在结尾添加空字符

2.3中的例子如果换成getline则可以解决问题。

  • get()

与getline()不同的是,换行符不会被丢弃。所以要实现2.3的例子,需要另外再加一个读取换行符的操作。

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

我们还可以使用下列的方式来避免这种情况:

cin.get(name, ArSize).get();//增加一次读取

同样情况getline也会有同样的操作:

cin.getline(name1,ArSize).getline(name2, ArSize);//两次读取

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

3. string类字符串

string类型的变量是对象,而不是字符数组。string类位于命名空间std中。

string str1;
string str2 = "panther";

3.1 C++11z字符串初始化

char first[] = {
    
    "I am li hua"};
char second[] {
    
    "I am li hua"};
string third = {
    
    "I am li hua"};
string fourth {
    
    "I am li hua"};

3.2 赋值、拼接和附加

使用string类时,某些操作更简单。例如,不能将一个数组赋给另一个数组,但可以将一个string对象赋给另一个string对象:

  • 赋值
char char1[20];
char char2[20] = "panther";
string str1;
string str2 = "panther"
char1 = char2;//不可以!!!
str1 = str2:
  • 拼接和附加

可以使用+进行拼接,还可以使用+=操作进行附加。

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

在C风格中拼接和附加可以使用strcpy和strcat函数。类似的还有strlen()和str.size()的关系。

3.3其它形式的字符串字面值

C++中还有类型wchar_t,而C++11中还新增了char16_t和char32_t。他们分别使用前缀L、u、U表示字符串字面值。

wchar_t title[] = L"I am li hua";
char16_t name[] = u"I am li hua";
char32_t car[] = U"I am li hua";

4. 结构简介

4.1 定义和使用

定义结构,并使用它创建变量。

    struct inflatable
    {
    
    
        char name[20];
        flaot volme;
        double price;
    };

struct inflatable goose;//C-风格
inflatable hat;//C++风格创建变量

也可以在定义结构的时候创建变量。

    struct inflatable
    {
    
    
        char name[20];
        flaot volme;
        double price;
    }goose, hat;
struct inflatable
    {
    
    
        char name[20];
        flaot volme;
        double price;
    }goose = 
    {
    
    
"li hua",
0.2,
30
};

还可以创建没有名称的结构类型。

    struct inflatable
    {
    
    
        char name[20];
        flaot volme;
        double price;
    };

4.2 初始化

inflatable guest = 
{
    
    
	"Glorious Floria",
	1.88,
	28.99
};

当然也可以放在同一行

inflatable guest = {
    
    "Glorious Floria", 1.88, 28.99};

C++11结构初始化,“=”号是可选项。

inflatable guest {
    
    "Glorious Floria", 1.88, 28.99};

4.3 结构中的位字段

结构中的变量可以指定位数,这种通常用在低级编程中。

struct torgle_register
{
    
    
	unsigned int SN:4;//4位
	unsigned int4;//4位
	bool goodIn:1;//1位
	bool goodTorgle://1}

5. 共用体

共用体(union)是一种数据结构,它能够存储不同的数据类型,但只能同事存储其中一种类型。它的句法与结构相似,但含义不同。它的长度为其最大成员的长度。

下来声明可以使用one4all变量来存储int、long或double。

union one4all
{
    
    
	int int_val;
	long long_val;
	double double_val;
};
one4all pail;//定义

共用体用于节省内存。当前,系统的内存多达数GB甚至TB,好像没有必要节省内存。但是在嵌入式系统程序,如控制烤箱、MP3播放器来说,内存可能非常宝贵。

6. 枚举

C++的enum工具提供了另一种创建符号常量的方式,可以替代const。例如

enum spectrum{
    
    red, orange, yellow, green};
  • spectrum 被称为枚举类型
  • 他们的元素被称为枚举量,red从0开始,依次递增1
  • 使用和结构、共用体类似。如下:
#include<iostream>
using namespace std;
int main()
{
    
    
   enum spectrum{
    
    red, orange, yellow, green};//定义新类型

   spectrum band;//申明一个spectrum类型变量
   band = orange;//可以
   int color = red;//可以
   color = red +3;//可以
   band  = spectrum(3);//可以,强制类型转换

   /*
   ++band;//不可以修改值
   band = red + orange;//不可以
   band = 3;//不可以
   */

}

  • 枚举的特殊形式

可以使用赋值运算符来显式设置枚举量的值:

enum bits{
    
    one = 1, two = 2, four = 4, eight = 8};
//first默认为0,third根据second递增,third==101
enum bitstep{
    
    first, sencond = 100, third};

还可以创建多个值相同的枚举量:

//zero == null ==0; one == numer_uno ==1;
enum {
    
    zero, null = 0, one, numer_uno = 1};
  • 枚举的取值范围

只要在范围内的值可以进行强制转换:

enum bits{
    
    one = 1, two = 2, four = 4, eight = 8};
bits myflag;
my flag = bits(6);//合法

其中bits的范围根据幂次方来决定:

  • 最大值:大于最大枚举量的最小2的幂,将它减1。
  • 最小值:如果枚举量不小于0,则最小值为0;如果小于0,则与最大值相同的方式查找。

如果最小枚举量为-6,那么最大的2的幂为-8,因此下限为-7。
如果最大枚举量为101,那么最小的2的幂为128,因此上限为127。

7. 指针和自由存储空间

存储数据的3中基本属性

  • 信息存储在何处;
  • 存储值是多少;
  • 存储的信息是什么类型。

指针是一个变量,它存储的是一个地址。

7.1 声明和初始化指针

int *p1,p2;

其中p1表示指针,指向了int类型,而p2为普通int变量。

初始化:

int higgens = 5;
int *ptr = &higgens;

7.2 指针和数字

int *pt;
pt = 0xB8000000;//C99标准之前,C语言允许这样的操作,C++不允许。
pt = (int*)0xB8000000;//可以

7.3 使用new来分配内存

C语言中,可以用库函数malloc()来分配内存,当然C++也允许这么做,但C++有更好的方法——new运算符。

int *pn = new int;//开辟一块int大小的内存。

new从堆(heap)中分配内存。如果没有足够的内存而无法满足new的请求,这种情况被称为内存耗尽,无法分配新的内存,它会返回一个空指针(null pointer)。

7.4 释放内存

int *ps = new int;//分配内存
delet ps;//释放内存

释放ps指向的内存,不会删除ps本身,还可以用它指向另一个新分配的内存块。只不过把new出来的内存给释放了。

内存泄露:一定要配对的使用new和delete,否则将发生内存泄露(memory leak),也就是说,被分配的内存再也无法使用了。如果内存泄露严重,则程序将由于不断寻找更多内存而终止。

只能用delete来释放使用new分配的内存,而且对空指针使用delete是安全的。

下面这个例子也行,因为我们释放的是new创建的地址,和指针无关!

int *ps = new int;
int *pd = ps;
delete pd;

7.5 使用new来创建动态数组

我们知道静态数组在编译的时候就需要定义大小。但创建动态数组则可以根据运行时来决定数组大小。

  • 使用new创建动态数组

创建动态数组:

int *psome = new int [10];//psome指向数组的第一个元素
//这里的10可以动态分配

释放动态数组:

delete [] psome;//释放的是整个数组,而不是指针指向的元素
  • 使用动态数组

类似于普通数组,使用的方式为psome[0],psome[1]。但是要注意数组名不可以改变,指针是可以改变的。比如psome+1,那么psome[0]将发生变化。例如:

#include<iostream>
using namespace std;
int main()
{
    
    
    double *p3 = new double [3];
    p3[0] = 0.2;
    p3[1] = 0.5;
    p3[2] = 0.9;
    cout<<"p3[0]= "<<p3[0]<<endl;
    p3++;
    cout<<"After p3++,p3[0] = "<<p3[0]<<endl;
    
	p3--;
    delete [] p3;//记得删除,养成习惯
    return 0;
}

输出:
在这里插入图片描述

  • 数据的存储方式

C++中有3种管理数据内存的方式:自动存储、静态存储和动态存储。

C++11新增了第四种类型——线程存储。后续会讲到。

  • 自动存储
    函数内部定义的常规变量,使用自动存储空间,被称为自动变量,也叫局部变量。通常存储在栈中,属于后进先出(LIFO)。
  • 静态存储
    静态存储在整个程序执行期间都存在的存储方式。
    static double fee = 56.67;
  • 动态存储
    new和delete运算符提供的一直能够比动态变量和静态变量更灵活的方法。它存储在堆(heap)中。
    在栈中,自动添加和删除机制使得占用的内存总是连续的。但new和delete的相互影响可能导致占用的自由存储区不连续。

总览目录
上一篇:(二)处理数据
下一篇:(四)循环和分支语句


文章参考:《C++ Primer Plus第六版》

猜你喜欢

转载自blog.csdn.net/QLeelq/article/details/112294340