C++入门——Day3_复合类型(有史以来写的最长的一篇博客)

一、数组

数组是一种数据格式,能够存储多个同类型的值。每个值都存储在一个独立的数组元素中,计算机在内存中一次存储数组的各个元素。

1:声明数组需要以下三点


·存储在每个元素中的值的类型

·数组名

·数组中的元素个数

声明数组的通用格式:

typeName arrayName[arraySize];

比如:short month[12];

*数组最后一个元素的索引比数组的长度小1

下面举个例子:
 

#include <iostream>

int main(void)
{
    using namespace std;

    int yams[3];
    yams[0] = 7;
    yams[1] = 8;
    yams[2] = 6;

    int yamcosts[3] = {20,30,5};
    cout << "Total yams = " << yams[0] + yams[1] + yams [2] << endl;
    cout << "The package with " << yams[1] << " yams costs " << yamcosts [0] << " cents per yams " << endl;

    int total = yams[0] * yamcosts[0] + yams [1] * yamcosts[1] + yams[2] * yamcosts[2];
    cout << "The total yam expense is " << total << " cents." << endl;
    cout << "Size of yams array = " << sizeof yams << " bytes." << endl;  //每个int有4个字节
    cout << "Size of one element = " << sizeof yams[0] << " bytes." << endl;  

    return 0; 
}

Total yams = 21
The package with 8 yams costs 20 cents per yams
The total yam expense is 410 cents.
Size of yams array = 12 bytes.
Size of one element = 4 bytes.

*sizeof返回的是整个数组的字节数

2:数组的初始化规则

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

初始化数组时,提供的值可以少于数组的元素数目,比如int a[5] ={1,2,3};

如果只对数组的一部分进行初始化,则编译器将把其他元素设置为0,因此0数组设置非常简单,只需要显示的把第一个元素设为0,long total[500] = {0};

如果初始化数组时,方括号内为空,则C++编译器会计算元素个数,比如

short things[] ={1,5,3,8},则编译器会使得things数组个数为4

3:C++11数组初始化方法

1)初始化数组时,可以省略等号(=)

double earning[4] {1,2,3,4}

2)可以不在大括号内包含任何东西,就默认为0;

3)列表初始化禁止缩窄转换,比如

long plifs[] = {25,92,3.0},浮点数转化为整型是缩窄操作,即使浮点数的小数部分为0

char slifs[4]={'h','i','112201','\0'},112201超出取值范围了

char tlifs[4] = {'h', 'i', 112, '\0'},这个是可以通过的

二、字符串

字符串是存储在内存的连续字节中的一系列字符,C++处理字符串的方式有两种,第一种来自C语言,称为C-style string 。

存储在连续字节中的一系列字符意味着可以将字符串存储在char数组中,其中每一个字符都位于自己的数组元素中。C风格字符串具有一种特殊的性质,以空字符(\n)结尾的,其ASCII码为0,用来标记字符串的结尾

比如:

char dog[8] = {'b','e','a','u','x',' ','I','I'};
char cat[8] = {'f','a','t','e','s','s','a','\0'};

只有cat数组才算字符串,因为如果是dog数组,打印到最后一个数组可能会一直打印,直到遇到空字符才会停止,所以\0放在结尾才算字符串

但是如果每个字符串是上面那样的字符数组表示出来,会特别复杂,因此有一种新的初始化字符串的方法,只需要使用一个用括号括起的字符串即可,这种字符串被称为字符串常量或字符串字面值

如下:char bird[11]="Mr.Cheeps";它会在末尾隐式地自动增加\0。

或者不写大小,char fish[] = "Bubbles";

*在确定存储字符串所需地最短数组时,别忘了将结尾空字符算在内

还要注意区别字符串常量和字符常量:
字符常量('S')是字ASCII码83的另一种写法,但是字符串常量"S"实际上表示两个字符(字符S和\0)组成的字符串

1:拼接字符串常量

任何有两个空白分隔(空格,制表符,换行符)的字符串常量都将自动拼接成一个

2:在数组中使用字符串

要将字符串存储在数组中,有两种方法,下面程序展示了这两种

#include <iostream>
#include <cstring>

int main(void)
{
    using namespace std;

    const int Size = 15;
    char name1[Size];
    char name2[Size] = "C++owboy";

    cout << "Howdy!I'm " << name2 << ". What is your name ?" << endl;
    cin >> name1;
    cout << "Well, " << name1 << ", your name has " << strlen(name1) << " letters." << endl;  
    cout << "And your name is stored in an array of " << sizeof(name1) << " bytes";   

    return 0; 
}

strlen是定义在cstring下的方法,它只打印出存储在数组的字符串的长度,只计算可见字符

sizeof是指出了数组占用的大小,这里是15个

输出:

Howdy!I'm C++owboy. What is your name ?
basciman
Well, basciman, your name has 8 letters.
And your name is stored in an array of 15 bytes

3:字符串输入

程序string.cpp有一个缺陷,下面的程序来揭露它

#include <iostream>
#include <cstring>

int main(void)
{
    using namespace std;

    const int ArSize = 20;

    char name[ArSize];
    char dessert[ArSize];

    cout << "Enter your name : " << endl;
    cin >> name;
    cout << "Enter your favorite dessert : " << endl;
    cin >> dessert;
    
    cout << "I have some delicious " << dessert << " for you ," << name << "." << endl;

    return 0; 
}

Enter your name :
Alistair Dreeb
Enter your favorite dessert :
I have some delicious Dreeb for you ,Alistair.

这里输入名字以后,直接跳过甜品,而直接执行最后一个,出现了漏洞

原因是:cin其实是通过空白字符来判断字符串是否结束了,在Alistair之后,看到了空格,判断当前字符串已经结束,所以只读取了第一个单词,而将后面的部分(Dreeb+换行符Enter)放在了计算机的缓冲区了,下面的cin就直接跳过了,而把Dreeb存放在dessert数组中

4:每次读取一行字符串输入

istream中的类提供了一些面向行的类成员函数:getling()和get()

这两个函数都读取一行的输入,直到到达换行符,随后getline会丢弃换行符,而get会把换行符保留在输入序列中。

1)面向行输入:getline()

它通过回车键输入来确定结尾。要调用这种方法,可以使用cin.getline()

该函数有两个参数,第一个参数是用来存储输入行的数组名称,第二个参数是要读取的字符数,如果这个参数是20,那么函数最多读取19个字符,余下的空间用于添加空字符。

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

cin.getline(name,20);

#include <iostream>
#include <cstring>

int main(void)
{
    using namespace std;

    const int ArSize = 20;

    char name[ArSize];
    char dessert[ArSize];

    cout << "Enter your name : " << endl;
    cin.getline(name,ArSize);
    cout << "Enter your favorite dessert : " << endl;
    cin.getline(dessert,ArSize);
    
    cout << "I have some delicious " << dessert << " for you ," << name << "." << endl;

    return 0; 
}

通过引用了cin.getling()函数,就可以完美的解决上面的问题了

2)面向行输入:get()

该函数有多种变体

①其中一种变体的工作方式与getline()类似,它们接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但get不会丢弃换行符,假设连续两次调用get()。

cin.get(name,ArSize);

cin.get(dessert,ArSize); 

//这样就有问题了,第一次输入完名字敲了回车,名字会放在名字数组,但是就会把回车留在缓冲区,再次捕获时就会把回车放进去了

②为了解决上述问题,get()有另一个变体,使用不带任何参数的cin.get()调用可读取下一个字符(即使是换行符),可以用它来处理换行符,因此可以采用以下的方式调用序列:

#include <iostream>
#include <cstring>

int main(void)
{
    using namespace std;

    const int ArSize = 20;

    char name[ArSize];
    char dessert[ArSize];

    cout << "Enter your name : " << endl;
    cin.get(name,ArSize);
    cin.get();  //read enter
    cout << "Enter your favorite dessert : " << endl;
    cin.getline(dessert,ArSize);
    
    cout << "I have some delicious " << dessert << " for you ," << name << "." << endl;

    return 0; 
}

编译结果:

Enter your name :
Dirk Hammernose
Enter your favorite dessert :
chicken
I have some delicious chicken for you ,Dirk Hammernose.

③另一种使用get()的方式是将两个类成员函数拼接起来

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

同样可以采用以下的拼接方式

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

但是这样再检查起来感觉比较麻烦。。

总之,getling()使用起来更简单,但是它无法分别出停止读取原因是满了还是遇到换行符了,但是get()就可以,它使得检查错误更简单些

*需要指出的是,C++允许函数有多个版本,条件是这些版本的参数列表不同,如果使用的是cin.get(name.ArSize),则编译器直到是将一个字符串放入数组中,因而将使用适当的成员函数。如果使用的是cin.get(),则编译器直到是读取一个字符,这种特性叫重载(参数不同,函数相同)

5:混合输入字符串和数字

请看下例:

#include <iostream>

int main(void)
{
    using namespace std;

    int year;

    cout << "What year was your house built? " << endl;
    cin >> year;
    cout << "What is its street adress? " << endl;    
    char adress[80];
    cin.getline(adress,80);

    cout <<"Year built: " << year << endl;
    cout << "Adress: " << adress << endl;

    return 0; 
}

What year was your house built?
1966
What is its street adress?
Year built: 1966
Adress:

还不等我们输入地址,就出现了上面个的问题,其实也是因为上面cin并不会自动删除换行符,把换行符留在了缓冲区,解决方法就是再用一个get()把换行符捕获!

#include <iostream>

int main(void)
{
    using namespace std;

    int year;

    cout << "What year was your house built? " << endl;
    cin >> year;
    cin.get();
    cout << "What is its street adress? " << endl;    
    char adress[80];
    cin.getline(adress,80);

    cout <<"Year built: " << year << endl;
    cout << "Adress: " << adress << endl;

    return 0; 
}

这样就解决了,或者直接把get()放在cin后面即可

三、string类简介

要使用string类,必须在程序中包含头文件string。string类位于命名空间std中,看程序

#include <iostream>
#include <string>

int main(void)
{
    using namespace std;

    char charr1[20];
    char cahrr2[20] = "jaguar";
    string str1;
    string str2 = "panther";

    cout << "Enter a kind of feline: ";
    cin >> charr1;
    cout << "Enter another kind of feline: ";
    cin >> str1;

    cout << "Here are some felines: \n";
    cout << charr1 << " " << cahrr2 << " " << str1 << " " << str2 << endl;

    return 0; 
}

结果:

Enter a kind of feline: ocelot
Enter another kind of feline: tiger
Here are some felines:
ocelot jaguar tiger panther

因此可以知道:

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

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

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

·可以使用数组表示法来访问存储在string对象中的字符

*使用string类可以自动地调整string的长度

1:赋值、拼接和附加

某些操作比使用数组时更加简单

例如不能把一个数组赋值给另一个数组,但是可以将一个string对象赋值给另一个string对象

str1 = str2;

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

见如下操作:

#include <iostream>
#include <string>

int main(void)
{
    using namespace std;

    string s1 = "penguin";
    string s2,s3;

    cout << "You can assign one string object to another : s2 = s1" << endl;
    s2 = s1;
    cout << "s1: " << s1 << ", s2: " << s2 << endl; 
    cout << "You can assign a C-style string to a string object" << endl;//赋值
    s2 = "buzzard";
    cout << "s2: " << s2 << endl;

    cout << "You can concatenate strings: s3= s1 + s2 " << endl;  //拼接
    s3 = s1 + s2;
    cout << "s3: " << s3 << endl;

    s1 += s2;
    cout << "s1 += s2 yields s1 = " << s1 << endl; //追加

    s2 += " for a day";
    cout << "s2: " << s2 << endl;                   //直接追加新增
    

    return 0; 
}

结果:

You can assign one string object to another : s2 = s1
s1: penguin, s2: penguin
You can assign a C-style string to a string object
s2: buzzard
You can concatenate strings: s3= s1 + s2
s3: penguinbuzzard
s1 += s2 yields s1 = penguinbuzzard
s2: buzzard for a day
 

2:string类的其他操作

下面我们试一下用字符数组来实现以上的操作,在没有string类之前,我们需要加入头文件cstring来提供这些函数,例如使用strcpy()将字符串赋值到字符数组中。使用strcat()函数将字符串附加到字符数组末尾,看下例:

#include <iostream>
#include <string>
#include <cstring>

int main(void)
{
    using namespace std;

    char charr1[20];
    char charr2[20] = "jaguar";
    string str1;
    string str2 = "panther";

    str1 = str2;
    strcpy(charr1,charr2);

    str1 += "paste";
    strcat(charr1,"juice");

    int len1 = str1.size();
    int len2 = strlen(charr1);

    cout << "The string " << str1 << " contains " << len1 << " characters." << endl;
    cout << "The string " << charr1 << " contains " << len2 << " characters." << endl;

    return 0; 
}

The string pantherpaste contains 12 characters.
The string jaguarjuice contains 11 characters.

由此可以看出string类的优势!

3:string类的I/O

我们可以使用cin和运算符<<来将输入存储到string对象中,使用cout来显示,这种是C风格,但是每次读取一行而不是一个单词的时,使用的句法不同,见下面的程序:

#include <iostream>
#include <string>
#include <cstring>

int main(void)
{
    using namespace std;

    char charr[20];
    string str;

    cout << "Length of string in charr before input : " << strlen(charr) << endl;  
    cout << "Length of string in str before input : " << str.size() << endl;  //这里打印出的结果就是0,

    cout << "Enter a line of text: " << endl;
    cin.getline(charr,20);
    cout << "You entered: " << charr << endl;

    cout <<"Enter another line of text: " << endl;
    getline(cin,str);
    cout << "You entered: " << str << endl;

    cout << "Length of string in charr after input : " << strlen(charr) << endl;
    cout << "Length of string in charr after input : " << strlen(charr) << endl;

    return 0; 
}

结果:

Length of string in charr before input : 1
Length of string in str before input : 0
Enter a line of text:
peanut butter
You entered: peanut butter
Enter another line of text:
blueberry jam
You entered: blueberry jam
Length of string in charr after input : 13
Length of string in charr after input : 13

*注意在没有初始化之前,只是开辟了内存空间,但是并没有把数组进行初始化,所以该字符串长度可能是随机的,一直寻找空字符停止

四、结构体

假设要存储有关篮球运动员的信息,则可能需要存储姓名、工资、身高、体重、平均得分等信息。希望有一种数据结构可以将所有的这些信息存储在一个单元中。这里面的数据可能是不同类型的数据,所以不能用数组来存储。

C++中的结构可以满足要求,结构是一种比数组使用更加灵活的数据格式,同一个结构可以存储多种类型的数据。如果要追踪整个球队,则可以使用结构数组,结构也是C++OOP类的基石。

比如:一个公司要创建一种类型来描述生产线上产品的成员,具体来说,这种类型应该存储产品名称,容量,售价,所以可以提供下面的结构体:

struct inflatable 
{
   char name[20];
   float volume;
   double price;
};

*右括号结束完了有“;”

定义完结构以后,就可以创建这种类型的变量了。

inflatable hat;
inflatable woopie_cushion;
inflatable mainframe;

由于hat类型为inflatable,所以可以使用成员运算符(.)来访问各个成员,比如hat.price,它们由于被声明为double类型,所以就相当于hat.price是一个double变量,总之,hat是一个结构,hat.price是一个double变量

1:在程序中使用结构

#include <iostream>

struct inflatable
{
    char name[20];
    float volume;
    double price;
};

int main(void)
{
    using namespace std;

    inflatable guest = 
    {
        "Glorious gloria",
        1.88,
        29.99
    };

    inflatable pal = 
    {
        "Audacious Arthur",
        3.12,
        32.99
    };

    cout << "Expand your guest list with " << guest.name << " and " << pal.name << endl;
    cout << "You can have both for $ " << guest.price + pal.price << endl;

    return 0; 
}

结果:
Expand your guest list with Glorious gloria and Audacious Arthur
You can have both for $ 62.98

*C++推荐把结构体放在外部

2:C++11结构初始化

C++11也支持将列表初始化用于结构,且(=)可选

inflatable duck {"Daphne","0.12","9.98"},这种方式也OK

3:结构可以将string类作为成员吗?

可以的

第一种方法是把using namespace std放在main函数外部

#include <iostream>

using namespace std;

struct inflatable
{
    string name;
    float volume;
    double price;
};

int main(void)
{
    inflatable guest = 
    {
        "Glorious gloria",
        1.88,
        29.99
    };

    inflatable pal = 
    {
        "Audacious Arthur",
        3.12,
        32.99
    };

    cout << "Expand your guest list with " << guest.name << " and " << pal.name << endl;
    cout << "You can have both for $ " << guest.price + pal.price << endl;

    return 0; 
}

第二种方法是std::string name

接下来介绍以下结构体赋值:

#include <iostream>

using namespace std;

struct inflatable
{
    string name;
    float volume;
    double price;
};

int main(void)
{
    inflatable bouquet = 
    {
        "sunflowers",
        0.20,
        12.49
    };

    inflatable choice;

    cout << "bouque: " << bouquet.name << " for $" << bouquet.price << endl;
    
    choice = bouquet;//结构体可以直接赋值

    cout << "choice: " << choice.name << " for $" << choice.price << endl;

    return 0; 
}

bouque: sunflowers for $12.49
choice: sunflowers for $12.49

甚至我们可以直接完成结构体定义和创建变量的工作,如下:

struct perks
{
   int key_number;
   char car[12];
} mr_smith,ms_jones;

还可以这样,定义完结构体直接就赋值,,,不过看去来太丑了,,,,,

struct perks
{
   int key_number;
   char car[12];
}mr_glitz=
{
   7,
   "Packard"
};

4:结构体数组

首先是一个数组,然后数组中的每一个元素都是结构体,厉害了

比如创建100个inflatable结构的数组,可以这样

inflatable gifts [100];

这样,gifts将是一个inflatable数组,其中的每一个元素都是inflatable对象,可以与成员运算符使用

cin >> gifts[0].volume;等操作

初始化方法:

inflatable guest[2] = 
{
    {"Bambi", 0.5 , 21.99},
    {"Godzilla",2000,565.99}
};

外层{}是数组成员括号,内层{}是结构体的初始化规则

例:

#include <iostream>

struct inflatable
{
    char name[20];
    float volume;
    double price;
};

int main(void)
{
    using namespace std;

    inflatable guest[2] = 
    {
        {"Bambi",1.88,21.99},
        {"Godzilla",2000,565.99}
    };

    cout << "The guests " << guest[0].name << " and " << guest[1].name << " have a combined volume of "
    << guest[0].volume + guest[1].volume << " cubic feet" << endl;   

    return 0; 
}

The guests Bambi and Godzilla have a combined volume of 2001.88 cubic feet

五、共用体

共用体(union)是 一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型

结构体可以同时存储int,long double,但是共用体只能存储int,long,double。与struct类似,但含义不同,如下声明:

union one4all
{
    int int_val;
    long long_val;
    double double_val;
};

可以使用one4all变量来存储int,long或double,条件是在不同的时间进行;

看如下代码:
 

#include <iostream>

using namespace std;

union one2all
{
    char ch;
    int n;
};

int main(void)
{
    one2all num;

    cout << "sezeof(num) = " << sizeof(num) << endl;
   
    num.ch = 'A';  //这时放置的是字符A
    cout << "num.ch = " << num.ch << endl;
    cout << "num.n = " << num.n << endl;  //结果是65,是A的ASCII码

    return 0;
}

①结构体的大小,取决于最大的那一个

②如果提前存了字符,后面打印整型,就会显示它的ASCII码

共用体的用途之一是,当数据项使用两种或更多格式(不同时使用),可以节省空间,比如说管理小商品目录,有一些ID是整数,有一些ID是字符串,可以这样做:

struct widget
{
    char brand[20];
    int type;
    union id
    {
        long id_num;
        char id_char[20];
    }id_val;
};
widget prize;
if(prize.type == 1)
cin >> prize.id_val.id_num;
else
cin >> prize.id_val.id_char;

结构体嵌套共用体了

共用体常用于节省内存,但是C++还能用于嵌入式,比如烤箱,MP3等等,还是需要考虑内存,联合体常用于操作系统数据结构或硬件数据结构

六、枚举(enum)

C++的枚举工具提供了另一种创建符号常量的方式(之前只能用const),它还允许定义新类型,如:

enum spectrum {red,orange,yellow,green,blue,violet,};

上述语句完成两项工作:

·让spectrum称为新类型的名称,它叫做枚举

·让red,yellow这些变成了符号常量,它们对应整数值0~7,这些常量叫做枚举量

用枚举名来声明这种类型的变量

spectrum band;

1:枚举变量有一些特殊的属性

①在不进行强制类型转换的情况下,只能将定义枚举时使用的枚举量赋值给这种枚举的变量:


band = blue; 可以的,因为blue是枚举量

band = 2000;不可以,因为2000不是枚举量

因此spectrum变量受限,只能有6个取值,把非num的值赋值给num会报错

②枚举只有赋值运算符,没有算数运算

band = orange;  可以的

++band; 不可以的

③枚举是整型,可被提升为Int类型,但是int类型不能自动转换为枚举类型(int的精度级别比枚举高)

int color = blue ;可以的,因为int类型的精度高

band = 3;不可以的

但是如果是这样的:band = spectrum(3),这种强制转换是可以的

我们一般使用枚举来定义相关的符号常量


2:设置枚举量的值

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

enum bits {one =1 ,two =2 ,three = 3 ,four =4};

enum bigstep{first , second = 100, third}

这里first默认为0,third为前面的+1,为101

3:枚举的取值范围

最初,对于枚举来说,只有声明中指出的那些值是有效的,然而,C++现在增加了可赋给枚举变量的合法值

enum bits{one =1 ,two = 2, four =4 , eight = 8};

bits flag;

flag = bits(6).

这样的也是合法的,虽然不是枚举值,但是在取值范围内

七、指针和自由存储空间

计算机程序在存储数据时必须跟踪的3种基本属性

①信息存储在哪里;

②存储的值是多少;

③存储信息是什么类型。

我们之前采纳的方案是,定义一个简单变量,声明语句指出了值得类型和符号名,还让程序为值分配内存,并跟踪该内存单元

接下来我们是用另一种策略:这种策略是以指针为基础,指针是一个变量,它存储的是值得地址,而不是值得本身。

我们首先看如何找到常规变量得地址,只需对变量应用地址运算符&,就能获取它得位置,例如home是一个变量,则&home是它得地址(取指运算符)。

程序如下:

#include <iostream>

int main(void)
{
    using namespace std;

    int donuts = 6;
    double cups = 4.5;

    cout << "donuts value = " << donuts << " and donuts adress = " << &donuts << endl;
    cout << "cups value = " << cups << " and cups adress = " << &cups << endl;
     
    return 0;
}

结果如下:

donuts value = 6 and donuts adress = 0x98959ff87c
cups value = 4.5 and cups adress = 0x98959ff870

这俩地址可以看出,是从7c→70,分别是7c,7d,7f,70,地址增加了4次,代表增加了4个字节,说明donuts变量增加了4个字节,因为Int类型是占用4个字节。

  *运算符被称为间接值或解除引用运算符,将其用于指针,可以得到该地址处存储的值(取值运算符)

例如:

#include <iostream>

int main(void)
{
    using namespace std;

    int updates = 6;
    int *p_updates;  //声明一个指针
    p_updates = &updates;  //将updates的地址给前面声明的指针

    cout << "Value : update = " << updates << endl;
    cout << "*p_updates = " << p_updates << endl; //*p_updates = 0xc7007ffae4

    cout << "Adress : updates = " << &updates << endl; // *p_updates = 0xc7007ffae4 ,和上面的地址一样

    cout << "*p_updates = " << *p_updates << endl;  //指针里面的内容就是6

    *p_updates = *p_updates + 1;
    cout << "Value : update = " << updates << endl; //修改了值以后再看原updates的值,变成了7

    return 0; 
}

结果:

Value : update = 6
*p_updates = 0xb66c9ffd94
Adress : updates = 0xb66c9ffd94
*p_updates = 6
Value : update = 7

首先声明一个值,然后把它的地址通过&给一个指针,这个指针再用*就又回到了这个值,操作*p_updates就等同于操作int变量的updates。

1:声明和初始化指针

例如:

int * p_updates;

这样声明出来的p_update是指向int的指针或int *

而*p_updates是int,不是指针

*的位置可以放的比较随意,对于C程序员,更喜欢int *ptr,C++程序员更喜欢int* ptr

而int * ptr 和int*ptr都是可以的

如果声明两个指针,必须要每个加*,例如:
int *p1, *p2,而不能是 int *p1, p2,这样就声明成为了一个指针,一个int变量

对于double * tax_ptr 和 char * str来说,它们是两种存储长度不同的数据类型,但是这两个变量本身的长度通常是相同的,也就是说地址的长度相同

2:指针的危险

long * fellow;

*fellow = 223323;

fellow是一个指针,但是没有给它定义地址,所以要把数字放在哪儿是个问题,由于fellow没有被初始化,它可能有任何值,可能是电脑随机分配的,可能是把之前的内容修改了,所以一定要注意!所以指针初始化一定要是一个确定的,可用的地址

3:指针和数字

指针不是整型,虽然计算机通常把地址当作整数来处理,从概念上看,指针和整数是截然不同的类型。整数可以进行加减乘除,而指针描述的是位置,地址的运算毫无意义

例如:

int * pt;

pt = 0xB8000000;如果这样做,计算机会认为右边为整数,实际上也是整数,会报错

如果非要这么做,可以这样修改,强制类型转化把整数变成地址。pt = (int *) 0xB8000000

4:使用new来分配内存

指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。

在C语言中,我们可以用库函数malloc()函数来分配内存,在C++中仍然可以使用。但是我们有更好的方法——new运算符。

在运行阶段为一个int值分配未命名的内存,并使用指针来访问这个值。程序员要告诉new,需要为哪种数据类型分配内存;new将找到一个长度正确的内存块,示例如下:

int * pn = new int;

现在new int 告诉程序,需要适合存储int的内存,new运算符根据类型来确定需要多少字节的内存,然后找到这样的内存,并返回其地址。接着把地址赋给pn,pn是被声明为指向int的指针,现在pn是地址,而*pn是存储在那儿的值

通用格式如下:
typeName * pointer_name = new typeName;

其实不仅限于一些基本的类型,比如枚举啊,结构啊这种,都能使用new来开辟内存空间

看如下程序:

#include <iostream>

int main(void)
{
    using namespace std;

    int nights = 1001;
    int * pt = new int; //给pt创造了地址
    *pt = 1001;

    cout << "nights value = " << nights << " ; location = " << &nights << endl;
    cout << "int value = " << *pt << " ;  location = " << pt << endl;

    double *pd = new double;
    *pd = 10000001.0;

    cout << "double value = " << *pd << " ; location = " << pd << endl;

    cout << "location of pointer pd = " << &pd << endl;//指针的地址

    cout << "size of pt = " << sizeof(pt) << endl;  //8
    cout << "size of *pt = " << sizeof(*pt) << endl;    //4

    cout << "size of pd = " << sizeof(pd) << endl;   //8
    cout << "size of *pd = " << sizeof(*pd) << endl;   //8

    return 0; 
}

结果如下:
nights value = 1001 ; location = 0x67f7fffcf4
int value = 1001 ;  location = 0x2564fda1750
double value = 1e+07 ; location = 0x2564fda1770
location of pointer pd = 0x67f7fffce8
size of pt = 8
size of *pt = 4
size of pd = 8
size of *pd = 8
 

*这个就比较有意思了

我们的pt和pd都是new出来的指针,所以可以通过取值运算符&来计算指针的地址,虽然这个指针是存放地址了

后面我们分别打印了指针和内容的大小,可以看出,无论存放int还是double类型,我们指针都占用了8个字节

5:使用delete释放内存

当需要内存时,可以使用new来请求,使用delete后,将释放ps指向的内存,但不会删除指针ps本身,还可以把其他的内存块分配,一定要配对的使用new和delete,否则会发生内存泄漏

int * ps = new int ;

..

delete ps;

注意以下三点:


int * ps = new int ;

delete ps; 这是可以的,

delete ps ; 这是不可以的,不能删除两次!

int jugs = 5;

int * pi = &jugs;

delete pi ;这是不可以的,delete必须和new一起使用,成对的出现,不能使用Delete删除不是new的地址

int * ps = new int ; 

int * pq = ps ; 

delete pq;

这样是不行的,两个指针指向了同一个地址,其实本来就不行

6:使用new来创建动态数组

通常,对于一些比较简单的变量,我意思咱还是别用new了,一般用new是来管理大型数据的(比如数组、字符串、结构等),这也是new的用武之地

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

②但是如果使用new,如果在运行阶段需要数组,则创建它,如果不需要,则不创建。还可以在程序运行时选择数组的长度,这种被称为动态联编(dynamic binding),这种数组就称为动态数组

1)使用new创建动态数组

只要将数组的元素类型和数目告诉new即可

int * psome = new int [10];

new运算符这时候返回第一个元素的地址,然后给指针psome

使用完new分配的内存块时,使用delete释放即可

delete [] psome;

new和delete遵循的规则

·不要使用delete释放不是new分配的内存

·不要使用delete释放同一内存块两次

·如果使用new[] 为数组分配内存,应该使用delete [] 释放

·如果使用new[]为一个实体分配内存,应使用Delete释放

·对空指针应用delete是安全的

2)使用动态数组

在C和C++内部都使用指针来处理数组,所以只需要把指针当作数组名即可使用!牛逼吧

如下程序:

#include <iostream>

int main(void)
{
    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 += 1; //p3是指针啊,+1不就是把数组的位置+1,第一个就指向0.5而不是0.2了

    cout << "p3[0] is " << p3[0] << endl; //结果也是0.5

    p3 -= 1;
    delete [] p3; //释放内存

    return 0; 
}

p3 += 1; //       p3是指针,+1不就是把数组的位置+1,第一个就指向0.5而不是0.2了

我们可以通过修改指针来改变数组第一个元素指向的位置

八、指针、数组和指针算术

指针和数组基本等价的原因在于指针算数(pointer arithmetic)和C++内部处理数组的方式。

首先看一看算数,将整数变量+1后,其值增加1;但是将指针变量+1后,增加的量等于它指向的类型的字节数。将double类型的指针+1后,则数值将增加8,将指向short的指针+1后,则指针将增加2.

1:程序说明

看下例:

#include <iostream>

int main(void)
{
    using namespace std;

    double wages[3] = {10000.0,20000.0,30000.0};
    short stacks[3] = {3,2,1};

    double * pw = wages;//这里直接写数组名,指针就会自动存放数组中第一个元素的地址
    short * ps = &stacks[0];//这里采用了另一种方式,访问数组第一个元素的地址

    cout << "pw = " << pw << ", pw = " << *pw << endl;
    pw += 1;
    cout << "pw = " << pw << ", pw = " << *pw << endl; //这里其实是增加了double的字节长度,从对比地址的长度也可以看出来,所以就直接到20000.0

    cout << "ps = " << ps << ", ps = " << *ps << endl;
    ps += 1;
    cout << "ps = " << ps << ", ps = " << *ps << endl;//这里就是增加了short的字节长度

    cout << "stacks[0] = " << stacks[0] << ", stacks[1] = " << stacks[1] << endl;//访问数组下标的方式
    cout << "*stacks = " << *stacks << ", *(stacks + 1) = " << *(stacks + 1) << endl; //拿数组名当指针用,也是OK的

    cout << "size of wages array = " << sizeof(wages) << endl; 
    cout << "size of pw pointer = " << sizeof(pw) << endl;

    return 0; 
}

结果:

pw = 0xaa5c7ff600, pw = 10000
pw = 0xaa5c7ff608, pw = 20000
ps = 0xaa5c7ff5fa, ps = 3
ps = 0xaa5c7ff5fc, ps = 2
stacks[0] = 3, stacks[1] = 2
*stacks = 3, *(stacks + 1) = 2
size of wages array = 24
size of pw pointer = 8

*

·cout << " *stacks = " << *stacks << ", *(stacks + 1) = " << *(stacks + 1) << endl;

这里拿数组名当指针用,然后加了*求值也是OK的

·*(stacks + 1) ==stacks[1],它们是等价的

·cout << "size of wages array = " << sizeof(wages) << endl;

需要注意的是,这里并没有把wages当作指针,就是数组第一个元素地址,8字节,而是24字节.因为sizeof(wages)会自动变成检测数组的内存大小空间,就是三个double值,即24字节!

数组的地址:

·数组名被解释为第一个元素的地址,而对数组名取地址运算符的时候,得到的是整个数组的地址

比如:

short tell [10];

cout << tell << endl;     这是第一个元素的地址

cout << &tell << endl;    这是整个数组的起始地址

从数字上来说,他俩的地址是相同的,但是从概念上来说,&tell[0]是2字节块的地址,而&tell是一个20字节内存块的地址。从这个观点出发,表达式tell + 1就是把地址+2,而表达式&tell +1是把地址+20!

#include <iostream>

int main(void)
{
    using namespace std;

    short tell[10] = {1,2,3,4,5,6,7,8,9,10};

    cout << "tell = " << tell << endl;       //tell = 0x3d13dffd80
    cout << "tell + 1" << tell +1 << endl;  //tell = 10x3d13dffd82,很明显地址增加了2

    cout << "&tell = " << &tell << endl;  //&tell = 0xda5abffd90
    cout << "&tell + 1" << &tell + 1 << endl;  //&tell + 10xda5abffda4,跨过了20个字节!

    return 0; 
}

地址在注释里面了,很明显能看出来增加了2和20!

·short  (*pas)[10] = &tell;

如果有括号,意味着这是由10个short类型组成的数组

如果是 short  *pas[10] = &tell;

优先级规则将使pas先和[10]结合,导致pas是一个short指针数组,它包含10个指针

2:指针小结

1)声明指针

typeName * pointerName;

2)给指针赋值(3种)

pn = & bubble;     直接上取址运算符

pn = new char;     使用new初始化

pn = new double[20];     同理

3)对指针解除引用

就是获取指针当前的值,使用*运算符即可

4)区分指针和指针所指向的值

pn是指针,*pn是指针指向的值

5)数组名

C++将数组名视为数组第一个元素的地址

6)指针算数

C++允许将指针和整数相加,加1的结果等于原来的地址值加上指向的对象所占用的总字节数

7)数组的动态联编和静态联编

int tacos[10];     静态联编

int *pz = new int [10];

..

delete [] pz;     动态联编,使用完以后要主动释放

8)数组表示法和指针表示法

使用[]数组表示法等同于对指针解除引用

taco[0] ,这时候taco就是数组

数组名和指针变量都是如此,因此对于指针和数组名,既可以使用指针表示法,也可以使用数组的表示法,如下:

int * pt = new int [10];  //数组名是pt
*pt = 5;  //第一个位置元素为5
pt[0] = 6;  //这里就是拿指针当数组来用
pt[1] = 44;
int coats[10];  
*(coats + 4) = 12;//这里又拿数组名当指针来用

解释见注释

3:指针和字符串

数组和指针的特殊关系可以扩展到C-风格字符串。如下:

char flower[10] = "rose";

cout << flower << "s are red" << endl;

数组名是第一个元素的地址,因此cout语句中的flower是包含r的char元素的地址,但是呢,即使我们的cout知道它代表的是字符串的地址,它还要打印该地址处的字符,然后继续打印后面的字符,直到遇到空字符为止,但是如果不是char类型的就会打印0x123123之类的。

然后看后面的"s are red",为了与cout对字符串输出的处理保持一致,这个用引号括起来的字符串也应该是一个地址,上述代码不会将整个字符串发送给cout,而是发送了该字符串的地址。这意味着对于数组中的字符串、用双引号括起来的字符串常量和指针所描述的字符串,处理方式其实是一样的,都将传递它们的地址。与逐个传递字符串中的所有字符相比,工作量要小很多了

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

看如下程序:
 

#include <iostream>
#include <cstring>

int main(void)
{
    using namespace std;

    char animal[20] = "bear";  //定义了字符数组,然后给出了字符串
    const char *bird = "wren";        //加了const意味着,你可以把任意对象地址给bird指针,但是不能通过bird指针修改对象
    char *ps;

    cout << animal << " and " << bird << endl; 
    //animal会把bear中第一个字符的地址放在cout后面,但是这时候会把它存放的字符依次打出,直到遇到\0,
    //中间的and也会解释为字符串,把它的首字母地址提供了,然后依次打出了该字符串
    //第三个是char指针,同理,也会把它代表的字符串的首地址所包含的字符依次打印出来,直到遇到\0;

    cout << "Enter a king of animal : ";
    cin >> animal;
    cout << animal << endl;   //好像只有这一种方法有用了

    //cin >>ps;
    //cout << ps << endl;//这样会报错!因为你还是没有给ps分配地址,直接就赋值

    //cin >> bird;   //不可以通过bird指针来修改常量内容
    //cout << bird << endl;   

    ps = animal;//把animal的第一个地址给了ps,这样是OK的
    cout << ps << endl;

    cout << animal << " at " << (int*)animal << endl; //只要不是char类,就能够打印出地址,所以这里采用了强制转换为int*,注意这里是把地址的类型换了,所以要加*
    cout << ps << " at " << (int*)ps << endl;  //可以看出它们的地址是一样的

    cout << "After using strcpy\n";
    ps = new char[strlen(animal) + 1];  //用了strlen,把ps的长度new为比animal长1个
    strcpy(ps,animal);   //C语言中的cpy函数,把animal的地址给了ps
    cout << ps << " at " << (int*)ps << endl;
    delete []ps; 

    return 0; 
}

注释里面详细得写了所有得过程

4:使用new创建动态结构

”动态“意味着内存是在运行时,而不是编译时分配得。而且结构和类是比较像的,所以本次的技术也同样适用于类。

结构如下:
inflatable * ps = new inflatable;

需要注意的是,创建动态结构时,不能将成员运算符(.)用于结构名,因为这种结构没有名称,只知道它的地址。

C++专门为这种情况提供了一个运算符:箭头成员运算符(->,ps->name)。该运算符可用于指向结构的指针,就像点运算符可用于结构名一样(ps.name)

另一种访问结构成员的方法:(*ps).name

看下例:

#include <iostream>

struct inflatable
{
    char name[20];
    float volume;
    double price;
};

int main(void)
{
    using namespace std;

    inflatable *ps = new inflatable;//new了指针了
    cout << "Enter name of intlatable item: ";
    cin.get(ps->name,20);  //通过这种方式来访问name
    cout << "Enter volume in cubic feet: ";
    cin >> ps->volume;
    cout << "Enter price $: ";
    cin >> ps->price;
    
    cout << "Name: " << (*ps).name << endl;
    cout << "Volume: " << (*ps).volume << endl;
    cout << "Price: $ " << ps->price << endl;

    delete ps;

    return 0; 
}

演示了两种访问结构体成员的方式

结果如下:

Enter name of intlatable item: Fabulous
Enter volume in cubic feet: 20
Enter price $: 2
Name: Fabulous
Volume: 20
Price: $ 2

1)一个使用new和delete的示例

下面定义了一个函数getname(),该函数返回一个指向输入字符串的指针。该函数将输入读入到一个大型的临时数组中,然后使用new[]创建于一个刚好能存储该输入字符串的内存块,并返回一个指向该内存块的指针

#include <iostream>
#include <cstring>

using namespace std;

char * getname(void);

int main(void)
{
    char *name;

    name = getname();
    cout << name << " at " << (int*)name << endl;

    delete [] name;

    name = getname();
    cout << name << " at " << (int*)name << endl;
    delete [] name;

    return 0; 
}
char * getname(void)  //无参数,返回类型为char指针
{
    char tem[80];

    cout << "Enter last name : ";
    cin >> tem;

    char * pn = new char[strlen(tem)+1];
    strcpy(pn,tem);

    return pn;
    
}

结果:

Enter last name : Rick
Rick at 0x1c93c881750
Enter last name : Jack
Jack at 0x1c93c881750

对于getname()来说,它使用cin将输入的单词放到temp数组中,然后使用new分配新内存来存储该单词

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

1)自动存储

在函数内部定义的常规变量使用的自动存储空间,被称为自动变量。

它们在所属函数被调用时产生,函数结束时消失。比如getname()中的temp数组。

2)静态存储

静态存储是整个程序执行期间都存在的存储方式

使变量称为静态的方式有两种:一种是在函数外部定义它;另一种是在声明变量时使用static

3)动态存储

new和delete运算符,它们管理了一个内存池,这在C++中被称为自由存储空间或堆(heap)。这个内存池和静态变量和自动变量的内存不同。

*栈和堆的内存泄露

如果使用new运算符在自由存储空间(堆)上创建变量后没有调用delete,即使包含指针的内存由于作用域规则和对象生命周期的原因被释放,而在自由存储空间上动态分配的变量或结构也会继续存在,严重时会出现内存泄漏

解决方法就是new和delete要成对出现!

九、类型组合

数组、结构、指针,可以用各种方式组合它们

下面介绍结构先:

struct antarctica_years_end
{
   int year;
};

可以创建这种类型的变量

antarctica_years_end s01 , s02 , s03;

然后使用成员运算符访问其成员

s01.year = 1998;

可以创建指向这种结构的指针:

antartica_years_end * pa = &s02

pa指向了第二个结构的地址,创建指针后就可以通过间接成员运算符来访问成员:

pa->year =  1999;

也可以创建结构体数组

antartica_years_end trio[3];

然后通过成员运算符访问元素

trio[0].year = 2003;

其中,trio是一个数组,trio[0]是一个结构,trio[0].year是该结构的一个成员;由于数组名是一个指针,因此也可以通过间接成员运算符:

(trio+1) ->year = 2004;这和trio[1].year = 2004是一样的

可以创建指针数组:

const antarctica_years_end * arp[3] = {&s01,&s02,&s03};

既然arp是一个指针数组,arp[1]就是一个指针,所以可以用它访问成员:

cout << arp[1]->  << endl;

可创建指向上述数组的指针:

const antarctica_year_end ** ppa = arp;

例子:
 

#include <iostream>
#include <cstring>

using namespace std;

struct antarctica_years_end
{
    int year;
};

int main(void)
{

    antarctica_years_end s01,s02,s03;
    s01.year = 1998;

    antarctica_years_end *pa = &s02; //创建了该结构体类型的指针,肯定是用它的名字,它指向s02首地址
    pa ->year = 1999; //这里就使用了指针的用法访问结构体变量

    antarctica_years_end trio[3]; //这是创建了一个结构体类型的数组,成员都是结构体
    trio[0].year = 2003;//通过结构体来访问成员

    cout << trio->year << endl;//trio是数组第一个元素的地址,和这里的指针是一回事儿,这样出来就是第一个元素

    const antarctica_years_end * arp[3] = {&s01,&s02,&s03};   //三个常量结构体类型指针,不能通过该指针来修改指针指向对象内容
    cout << arp[1] ->year << endl;

    const antarctica_years_end **ppa = arp;  //指向结构体指针的指针,存放的还是地址,arp是第一个元素的地址,这里是取了地址的地址
    cout << (*ppa)->year << endl; //应该就是1998

    auto ppb = arp;//auto是自动识别,这里应该也是结构体指针数组
    ppb += 1;
    cout << (*ppb)->year << endl;

    return 0; 
}

结果:

2003
1999
1998
1999

这个程序后面比较抽象,我直接上一张图来分析

十、数组的替代品

1:模板类vector

模板类vector类似于string类,也是一种动态数组。可以在运行时设置vector对象长度,增加新数据,还能插入新数据。基本上是new创建动态数组的替代品,实际上vector类确实是使用了new和delete来管理内存的,不过这些工作是自动进行的

首先使用vector类,要有头文件<vectoer>,其次它包含在命名空间std中,看如下示例:

#include <iostream>
#include <vector>



int main(void)
{
    using namespace std;

    vector<int> vi;//使用vector类定义了int类型变量vi
    int n;
    cin >> n;
    vector<double> vd(n);

    return 0; 
}

一般而言,下面的声明创建一个名为vt的vector对象

vector<typeName> vt(n_elem);

n_elem代表它可存储n个elem类型为typeName的元素

2:模板类array(C++11)

vector功能比数组强大,但是效率比较低,如果是需要长度固定的数组,虽然数组是最佳的选择,但是代价是不太安全,所以有了array,它也是存在于std中,array对象的长度也是固定的,也使用栈(静态分配),而不是自由存储区(堆),因此效率和数组相同,但是更加安全,方便。

要使用array,同样要有头文件,语法创建和vector不完全相同,如下:

#include <iostream>
#include <array>

int main(void)
{
    using namespace std;

    array<int,5> ai;
    array<double,4> ad = {1.2,2.1,3.2,4.3};

    return 0; 
}

声明如下:

array<typeName,n_elem> arr;

3:比较数组,vector,array对象

给个例子,如下:

#include <iostream>
#include <array>
#include <vector>

int main(void)
{
    using namespace std;

    double a1[4] = {1.2,2.4,3.6,4.8};//传统方法

    vector<double> a2(4);//创建包含四个double元素的vector数组
    a2[0] = 1.0/3.0;
    a2[1] = 1.0/5.0;
    a2[2] = 1.0/7.0;
    a2[3] = 1.0/9.0;

    array<double,4> a3 = {3.14,2.72,1.62,1.41};
    array<double,4> a4;
    a4 = a3;   //array可以直接把a3的内容直接传递给a4,但是array就可以!

    cout << "a1[2]: " << a1[2] << " at " << &a1[2] << endl;
    cout << "a2[2]: " << a2[2] << " at " << &a2[2] << endl;
    cout << "a3[2]: " << a3[2] << " at " << &a3[2] << endl;
    cout << "a4[2]: " << a4[2] << " at " << &a4[2] << endl; //不管是哪种数组,都能用这种传统的表示方式来找到内容和地址

    a1[-2] = 20.2; //这时候并不是从0开始往后到1,2,而是往前走了两个double的长度,明显越界了,但是并没有报错,它不在数组的范围内
    cout << "a1[-2]: " << a1[-2] << " at " << &a1[-2] << endl;
    cout << "a3[2]: " << a3[2] << " at " << &a3[2] << endl;
    cout << "a4[2]: " << a4[2] << " at " << &a4[2] << endl;

    return 0; 
}

结果:

a1[2]: 3.6 at 0x6ed53ffbe0
a2[2]: 0.142857 at 0x1de87d71760
a3[2]: 1.62 at 0x6ed53ffba0
a4[2]: 1.62 at 0x6ed53ffb80
a1[-2]: 20.2 at 0x6ed53ffbc0
a3[2]: 1.62 at 0x6ed53ffba0
a4[2]: 1.62 at 0x6ed53ffb80

*a2[-2] =20.2明显越界了,但是并没有报错,因为C++并不会检查越界错误,但是还是有方法解决的

一种方案是使用成员函数at(),就像可以使用cin对象的成员函数getline()一样,也可以使用vector和array对象的成员函数at(),它可以在运行期间捕获非法索引,程序就会默认终端。

此外还有begin()和end()确定边界,以免无意间越界。

十一、复习题和编程题

复习题

1:如何声明下述数据?


a.actor是由20个char组成的数组

char actor[20];

b.betsie是 由100个short组成的数组

short betsie[100];

c.chuck是由13个float组成的数组

float chuck[13];

d.dipsea是由64个long double组成的数组

long double dipsea[64];

2:使用模板类array二不是数组来完成第一题

#include <array>;

array<char,20> actor;

array<short,100> betsie;

array<float,13> chuck;

array<long double,64> dipsea;

3:声明一个包含5个元素的int数组,并将它初始化为前5个正奇数

int a[5] = {1,3,5,7,9};

4:编写一条语句,将问题3中数组第一个元素和最后一个元素的和赋值给变量even;

int even = a[0] + a[4];

5:编写一条语句,显示float数组ideas中的第二个元素的值

float ideas[10];

cout << ideas[1] << endl;

6:声明一个char数组,将其初始化为字符串"cheeseburger".

char ch[15] = "cheeseburger";

char ch[] = "cheeseburger";这样页可以的,不用指定大小

7:声明一个string对象,并将其初始化为字符串“Waldorf Salad”

string a = "Waldorf Salad";

8:设计一个描述鱼的结构声明,结构中应包括品种,重量(整数,盎司)和长度(英寸,有小数)

struct fishes

{

     string kinds;

     short kg;

     foloat feet

};

9:声明一个问题8中定义的结构的变量,并对它进行初始化

fishes one ={"fox",2,1.4};

10:假设ted是一个double变量,请声明一个指向ted的指针,并使用它来显示ted的值

double ted = 1.8;

double *pointer = &ted;

cout << *pointer << endl;

11:用enum定义一个名为Response的类型,它包含Yes、No、Maybe等枚举量,yes为1,No为0,Maybe为2

enum Response {No,Yes,Maybe};

12:假设treacle是一个包含10个元素的float数组,请声明一个指向treacle的第一个元素的指针,并使用该指针来显示数组的第一个元素和最后一个元素。

float treacle[10];

float *pt = treacle;

cout << *pt[0] << "and" << pt[9] << endl;

13:编写一段代码,要求用户输入一个正整数,然后创建一个动态int数组,其中包含的元素数目等于用户输入的值。首先使用new来完成这项任务,再使用vector对象来完成任务

int size;

cout << "Enter a number: " << endl;

cin >> size;

int *pt = new int[size];

vector<int> dd(size)

14:下面的代码是否有效?

cout << (int*)"Home of the jolly bytes" ;

有效,是字符串的地址

15:编写一段代码,给问题8中描述的结构动态分配内存,再读取该结构成员的值。

struct fishes

{

     string kinds;

     short kg;

     foloat feet

};

fishes *pt = new fishes;

cout << (*pt).kinds << endl;

或者cout << pt->kinds << endl;

16:如下的程序清单,指出了混合输入数字和一行字符串时存储的问题,如果把

#include <iostream>

int main(void)
{
    using namespace std;

    int year;

    cout << "What year was your house built? " << endl;
    cin >> year;
    cin.get();
    cout << "What is its street adress? " << endl;    
    char adress[80];
    cin.getline(adress,80);

    cout <<"Year built: " << year << endl;
    cout << "Adress: " << adress << endl;

    return 0; 
}

cin.getline(adress,80);   替换为cin >> adress会发生什么影响?

getline()能获取一行的内容,一直到回车,如果只用cin,遇到空格就没了

17:声明一个vector对象和array对象,它们都包含10个string对象。指出头文件,但是不要使用using。使用const来指定要包含的string对象数

#include <string>;

#include <vector>;

#include <array>;



const int size = 10;

std::vector<std::string> str(size);

std::array<std::string,size> str;

*注意string也要用std命名空间

编程题

1:编写一个C++程序,如下述输出示例所示的请求并显示信息:

What is your first name ? Betty Sue

What is your last name ? Yewe

What letter grade do you deserve? B

What is your age ? 22

Name: Yewe , Betty Sue

Grade: C

Age: 22;

注意:该程序应该接受的名字包含多个单词,另外程序将向下调整成绩,即向上调一个字母

#include <iostream>
#include <string>

int main(void)
{
    using namespace std;

    const int size = 20;

    char fn[size];
    char ln[size];
    char gra;
    int age;

    cout << "What is your first name? ";
    cin.getline(fn,size);  //主要考察了getline()
    cout << "What is your last name? ";
    cin.getline(ln,size);

    cout << "What grade do you deserve? ";
    cin >> gra;

    cout << "What is your age?";
    cin >> age;

    cout << "Name : " << ln << " , " << fn << endl;
    cout << "Grade : " << char(gra+1) << endl;  //ASCII码加1显示的会是数字,所以进行强制转换
    cout << "Age: " << age << endl;

    return 0; 
}

结果:

What is your first name? Betty sue
What is your last name? Yewe
What grade do you deserve? B
What is your age?22
Name : Yewe , Betty sue
Grade : C
Age: 22
 

2:修改上一题的程序,使用C++string类而不是char数组

#include <iostream>
#include <string>

int main(void)
{
    using namespace std;

    string fn;
    string ln;
    char gra;
    int age;

    cout << "What is your first name? ";
    getline(cin,fn);
    cout << "What is your last name? ";
    getline(cin,ln);

    cout << "What grade do you deserve? ";
    cin >> gra;

    cout << "What is your age?";
    cin >> age;

    cout << "Name : " << ln << " , " << fn << endl;
    cout << "Grade : " << char(gra+1) << endl;  //ASCII码加1显示的会是数字,所以进行强制转换
    cout << "Age: " << age << endl;

    return 0; 
}

使用string类时候,只需要把

cin.getline(chat_Name,size)改成getline(cin,string_Name)即可

3:编写一个程序,它要求用户首先输入其名,然后输入其姓,然后程序使用一个逗号和空格把姓和名组合起来,并存储和显示组合结果,请使用string对象和头文件string中的函数

#include <iostream>
#include <string>

int main(void)
{
    using namespace std;

    string fn;
    string ln;

    cout << "Enter your first name? ";
    getline(cin,fn);
    cout << "Enter your last name? ";
    getline(cin,ln);

    cout << "Here is the information in a single string: " << ln << " , " << fn << endl;

    return 0; 
}

Enter your first name? Flip
Enter your last name? Fleming
Here is the information in a single string: Fleming , Flip

4:编写一个程序,它要求用户首先输入其名,然后输入其姓,然后程序使用一个逗号和空格把姓和名组合起来,并存储和显示组合结果,请使用char数组对象和cstring中的函数

#include <iostream>
#include <cstring>

int main(void)
{
    using namespace std;

    const int size = 20;

    char fn[size];
    char ln[size];

    cout << "Enter your first name? ";
    cin.getline(fn,size);
    cout << "Enter your last name? ";
    cin.getline(ln,size);

    cout << "Here is the information in a single string: " << ln << " , " << fn << endl;
    //或者再声明一个全字符串,然后用strcpy把几个拼起来就行了

    return 0; 
}

5:结构CandyBar包含三个成员。第一个成员存储了糖块的品牌,第二个成员存储糖块的重量(小数),第三个存储卡路里(整数)。声明结构并创建一个变量snack,并把其成员初始化为"Mocha Munch"、2.3、350。最后显示snack变量,初始化在声明时进行。

#include <iostream>
#include <string>

using namespace std;

struct CandyBar
{
    string brand; //或者char brand[20];
    float weight;
    int calo;
}snack;


int main(void)
{
    snack = {"Mocha Munch",2.3,350};
    cout << snack.brand << endl;

    return 0; 
}

结果:

"Mocha Munch"

6:结构CandyBar包含三个成员,如上提所示。请编写一个程序,创建一个包含三个元素的CandyBar数组(结构数组),并将它们初始化为所选择的值,然后显示每个结构的内容

#include <iostream>
#include <string>

using namespace std;

struct CandyBar
{
    string brand; //或者char brand[20];
    float weight;
    int calo;
};


int main(void)
{

    CandyBar candy[3]={{"a",1.2,100},{"b",2.2,200},{"c",3.3,300}};

    cout << candy[0].brand << endl;

    return 0; 
}

结果为a

就是创建结构数组,然后访问结构数组变量

7:William Wingate从事比萨饼分析服务,对于每个披萨饼,都需要记录下列信息:

公司名称

披萨直径

披萨重量

编写这种结构,程序要求用户输入上述信息。然后显示信息,请使用cin和cout或它的方法

#include <iostream>
#include <string>

using namespace std;

struct Pizza
{
    char company[20]; 
    float diameter;
    int weight;
};


int main(void)
{

    Pizza dinner;
    
    cout << "Enter the Pizza's company : ";
    cin.getline(dinner.company,20);
    
    cout << "Enter the Pizza's size: ";
    cin >> dinner.diameter;

    cout << "Enter the weight: ";
    cin >> dinner.weight;

    cout << "Company: " << dinner.company << endl;
    cout << "diameter: " << dinner.diameter << endl;
    cout << "weight: " << dinner.weight << endl;

    return 0; 
}

结果:

Enter the Pizza's company : asd
Enter the Pizza's size: 1.2
Enter the weight: 29
Company: asd
diameter: 1.2
weight: 29

8:完成上题,但是使用new来为结构分配内存,而不是声明一个结构变量。

#include <iostream>

using namespace std;

struct Pizza
{
    char company[20]; 
    float diameter;
    int weight;
};


int main(void)
{

    Pizza *pizza = new Pizza;//别忘了这是指针、

    cout << "Enter the Pizza's company : ";
    cin.getline(pizza->company,20);  //因为是指针,所以引用成员方法变了,其他的没变化,输出的同理

    delete pizza;

    return 0; 
}

如何用指针的形式访问成员,以及如何使用new,别忘了delete!

9:完成编程练习6,但使用new来分配动态数组,而不是声明一个包含三个元素的CandyBar数组

#include <iostream>
#include <cstring>

using namespace std;

struct CandyBar
{
    char brand[20];
    float weight;
    int calo;
};


int main(void)
{

    CandyBar *pt = new CandyBar[3]; //注意new和delete要成对出现,别忘了指针*
    strcpy(pt[0].brand,"Mocha Munch");//字符串拷贝函数,用指针代替数组
    pt[0].weight = 2.3;//后面两个都不是字符串了,可以直接赋值了
    pt[0].calo = 400;

    cout << "1.brand= " << pt->brand << endl;  //第一种访问方式
    cout << "1.weight= " << (*pt).weight << endl; //另一种访问方式

    delete []pt;  //释放时候要加[],因为是数组

    return 0; 
}

结果:

1.brand= Mocha Munch
1.weight= 2.3

*要注意strcpy赋值方法是给字符串用的,其他类型可以直接赋值!

10:编写一个程序,让用户输入三次100米跑的成绩,并显示次数和平均成绩,请使用array对象来存储数据。

#include <iostream>
#include <array>

int main(void)
{
    using namespace std;

    float average;
    array<float,3> record_list;

    cout << "First record: ";
    cin >> record_list[0];

    cout << "Second record: ";
    cin >> record_list[1];

    cout << "Third record: ";
    cin >> record_list[2];

    average = (record_list[0] + record_list[1] + record_list[2])/3;

    cout << "1st: " << record_list[0] << " 2nd: " << record_list[1] << " 3rd: " << record_list[2] << endl;
    cout << "average : " << average << endl;

    return 0;
}

*重点在于,创建array和把输入的值都赋给array的成员

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Laker404

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值