char dog[8]={'b','e','a','u','x',' ','I','I'}; //not a string
char cat[8]={'f','a','t','e','s','s','a','\0'}; //a string
这两个都是char类型的数组,但只有第二个才是字符串。
那么这种赋值比较复杂,还比较难输入。这就可以使用另一种方法将字符数组初始化为字符串。“ ”,这种字符串被称为字符串常量,或字符串字面值。
char bird[11]="Mr. Cheeps"; //\0 is understood
char fish[]="Bubbles";
用引号括起的字符串隐式地包括结尾的空字符,因此不用显示地包括它。
4.3 字符串输入
为了解决当遇到空格就提前结束的情况,提出了面向行输入的命令。
我们发现在cin输入字符串的时候,它仅仅是面向单词的输入方式。而我们在想输入字符串的时候,是想让它面向行的输入方式。
比较nb的是:istream中的类(如cin)提供了一些面向行的类成员函数:getline()和get().这两个函数都读取一行输入,直到到达换行符。
不同点:getline()将丢弃换行符,而get()将换行符保留在输入序列中。
1.面向行的输入:getline()
getline()函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。
cin.getline()该函数有两个参数,第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数。如果字符数是20的话,那么最多只能输入19个字符,余下的空间用于存储自动在结尾处添加的空字符。
char name[20];
cin.getline(name,20);
2.面向行的输入:get()
get()工作方式与getline()类似,接收的参数也都相同。当不同点是:get()并不再读取并丢弃换行符,而是将其留在输入队列中。
假如我们这样操作:
cin.get(name,20);
cin.get(dessert,20); //a problem
由于第一次调用后,换行符被留在了输入队列中,所以第二次调用时看到的第一个字符便是换行符。因此get()以为到了行尾,不再发现任何可读取的内容。
nb的是:get()有另外一种变体,使用不带任何参数的cin.get()调用可读取下一个字符。
cin.get(name ,20);
cin.get(); //便可跳过换行符
cin.get(dessert,20); //read second line
或者可以这样写:
cin.get(name,20).get();
3.混合输入字符串和数字出现的问题
当cin>>year;
之后会把换行符留在输入队列中,所以使用完cin>>year;之后,应该在后面添加一句:
cin.get(); //跳过换行符
4.string类简介
string对字符串的处理和c-风格的字符串之间的对比:
c-风格的字符串头文件#include
c-风格之间的一般操作:
//首先声明两个c风格的字符串数组
char charr1[20];
char charr2[20];
//复制字符串数组到另一个字符串数组中去
strcpy(charr1,charr2); //copy charr2 to charr1
//拼接字符串数组
strcat(charr1,charr2); //append contents of charr2 to charr1
//查看字符串数组中的大小
int len=strlen(charr1);
string 一般的操作为:
//声明两个string类型的变量
string str1;
string str2="nihao";
//复制字符串变量到另一个字符串变量中去
str1=str2;
//拼接字符串
str2+="xiaoming";
string str3=str2+str1;
//确定字符串中字符数
int len1=str1.size();
5.string类型的变量进行面向行的输入
前面所看到的cin.getline() ,cin.get()都是面向c-风格类型的字符串数组
接下来开始解析面向string类型的字符串变量进行面向行变量的输入赋值。
string name1;
getline(cin,name1);
4.4 结构简介
1.创建结构分为两步:定义结构和创建结构变量
比如定义结构:
struct inflatable
{
char name[20];
float volume;
double price;
};
定义:
inflatable gust={"Glorious Gloria",1.99,29.99};
//访问结构成员
cout <<gust.name<<gust.volume<<gust.price<<endl;
2.作用域
注意当外部声明的时候,可以用在文件的所有函数中。作用域是整个cpp从top到down的位置。
当内部声明的时候,是作用域是声明所在函数的位置内部。
3.其他结构属性
可以使用赋值运算符(=)将结构赋给另一个同类的结构,这样结构中的每个成员都将被设置为另一个结构中相对应成员的值。
struct inflatable
{
char name[20];
float volume;
double price;
}
inflatalbe bouquet={
"sunflowers",
0.20,
12.49
};
inflatable choice;
choice=bouquet; //同类结构变量可以赋值
也可以同时定义结构和创建结构变量,如:
struct perks
{
int key_number;
char car[12];
}mr_smith, ms_jones;//这里声明了两个结构变量
4.结构数组
这里定义一个inflatable类型的数组,里面的每一个变量都是inflatable类型。
inflatable guests[2]
{
{"Bambi",0.5,21.99},
{"Godzilla",2000,565.99}
};
4.5 共用体
共用体(union)是一种数据格式,他能够存储不同的数据类型,但只能同时存储其中的一种类型。
由于共用体每次只能存储一个值,因此他必须有足够的空间来存储最大的成员,所以共用体的长度为其最大成员的长度。
声明:
union one4all
{
int int_val;
long long_val;
double double_val;
};
可以使用one4all变量存储int、long或double,条件是在不同的时间进行。
one4all pail;
pail.int_val=15;
cout <<pail.int_val;
pail.double_val=1.38; //这个时候,上次存储的int\_val已经失效了。
cout<<pail.double_val;
1.使用共用体的场景
共用体常用于节省内存。另外,共用体常用操作系统数据结构或硬件数据结构。
4.6 指针和自由存储空间
关于面向对象编程和传统的过程性编程的区别的一些思考:
面向对象编程与传统的过程性编程的区别在于,OPP强调的是在运行阶段(而不是编译阶段)进行决策。运行阶段指的是程序正在运行时,编译阶段指的是编译器将程序组合起来时。运行阶段决策提供了灵活性。
比如:考虑为数组分配内存的情况。
传统的方法时声明一个数组,要在c++中声明数组,必须指定数组的长度。因此,数组长度在程序编译时就设定好了,这就是编译阶段决策。
OOP通过将这样的决策推迟到运行阶段运行,使程序更灵活,在程序运行后,可以这次告诉它只需要20个元素,而话可以下次告诉它需要200个元素。
总之,使用OOP,您可能在运行阶段确定数组的长度,为使用这种方法,语言必须允许在程序运行时创建数组。
1.指针
指针名表示的是地址。*运算符被称为间接值或解除引用运算符,将其应用于指针,可以得到该地址处存储的值。
2.声明和初始化指针
//下面的声明创建一个指针(p1)和一个int变量(p2)
int\* p1,p2;
**指针的危险:**在C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存。
3.指针和数字
要将数字值作为地址来使用,应通过强制类型转换将数字转换为适当的地址类型。如:
int \* pt;
pt=(int \*)0xB8000000;
4.使用new来分配内存
变量是在编译时分配的有名称的内存,而指针只是为可以通过名称直接访问的内存提供了一个别名。指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。
在c-语言和C++语言中都可以使用malloc()来分配内存。但C++还有更好的方法——new运算符。
在运行阶段为一个int值分配未命名的内存,并使用指针来访问这个值。程序员要告诉new,需要为哪种数据类型分配内存,new找到一个长度正确的内存块,并返回该内存块的地址。示例:
int \* pn =new int;
new int 告诉程序,需要适合存储int的内存。new运算符根据类型来确定需要多少字节的内存。然后它找到这样的内存,并返回其地址。
对于指针,需要注意的是:new分配的内存块通常与常规变量声明分配的内存块不同,常规变量都存储在被称为栈的内存区域中,而new从被称为堆或者自由存储区的内存区域分配内存。
5.使用delete释放内存
1.当需要内存时,可以使用new来请求。当使用完内存后,使用delete运算符,能够将器归还给内存池,归还或释放的内存可供程序的其他部分使用。
2.一定要配对地使用new,和delete,否则将发生内存泄漏,也就是说,被分配地内存再也无法使用了,另外,不能使用delete来释放声明变量所获得的内存。
3.不熬创建两个指向同一个内存块的指针,因为这将增加错误地删除同一个内存块两次地可能性。
6.使用new来创建动态数组
在编译时给数组分配内存被称为静态联编,意味着数组在编译时加入到程序中的。
但是用new时,如果在运行阶段需要数组,则创建它;如不需要,则不创建。还可以在程序运行时选择数组的长度,这被称为动态联编,意味着数组实在程序运行时创建的,这种数组叫做动态数组。
使用静态联编时,必须在编写程序时指定数组的长度;使用动态联编时,程序将在运行时确定数组的长度。
1.使用new创建动态数组
例如,要创建一个包含10个int元素的数组
int \* psome =new int [10];
当程序使用完new分配的内存块时,应使用delete释放他们。然而,对于使用new创建的数组,应使用另一种格式delete来释放
delete[] psome;
7.使用动态数组
int \* psome=new int [10];
如何访问其中的元素呢?
只要把指针当作数组名使用即可。也就是说,对于第一个元素,可以使用psome[0],而不是*psome;对于第二个元素,可以使用psome[1].
例如:
double \* p3 =new double [3];
p3[0]=0.2;
p3[1]=0.5;
p3[2]=0.8;
8.指针、数组和指针算术
- 将整数变量加1后,其值将增加1;但将指针变量加1后,增加的量等于它指向的类型的字节数。将指向double的指针加1后,若系统对double使用8个字节存储,则数值将增加8;将指向short的指针加1后,如果系统对short使用2个字节存储,则指针值将增加2.
- 对数组应用sizeof运算符得到是数组的长度,而对指针应用sizeof得到的是指针的长度,即使指针指向的是一个数组。
数组的地址
对数组取地址时,数组名也不会被解释为其地址。数组名被解释为其第一个元素的地址,而对数组名应用地址运算符&时,得到的是整个数组的地址:
short tell[10];
cout<< tell << endl; //显示的是数组中第一个元素&tell[0]的地址
cout << &tell <<endl; //显示的是整个数组的地址
虽然从数字上看,这两个地址相同,当从概念上讲,&tell0是一个2字节的内存块的地址。而&tell是一个20字节内存块的地址。
因此,表达式tell+1将地址值增加2,而表达式&tell+1将地址加20.换句话说,tell是一个short指针(*short),而&tell是一个这样的指针,即指向包含20个元素的short数组(short(*)[20]).也就是一个指针指向一个包含20个short元素的数组。
可以这样声明和初始化这种指针。