一、数组
数组是一种数据格式,能够存储多个同类型的值。每个值都存储在一个独立的数组元素中,计算机在内存中一次存储数组的各个元素。
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的成员