《C++ Primer Plus》读书笔记之二—复合类型
二、第四章 复合类型
1、C-风格字符串:C-风格字符串具有一种特殊的性质:以空字符结尾,空字符被写成\0,其ASC||编码为0,用来标记字符串的结尾。例如:
char dog[5]={'b','e','a','u','x'}; // not a string!
char cat[5]={'f','a','t','s','\0'}; // a string!
这两个数组都是char数组,但只有第二个数组是字符串。空字符串对C-风格字符串至关重要。如果使用coiut显示上面的cat这样的字符串,将显示前四个字符,发现空字符后停止。但是如果要显示dog数组(它不是字符串),cout将打印出数组的5个字符串,并接着将内存中随后的各个字节解释为要打印的字符,直到遇到空字符为止。
另外一种将字符数组初始化为字符串的方法,这种字符串被称为字符串常量:
char bird[10]="Mr.Cheeps";
char fish[]="Bubbles";
用引号括起的字符串隐式的包含结尾的空字符,因此不用显示的包括它。
注意:在确定存储字符串所需的最短数组时,别忘了将结尾的空字符计算在内。
2、每次读取一行字符串输入:istream中的类(如cin)提供了一些面向行的类成员函数:getline()和get()。这两个函数都读取一行输入,直到到达换行符。然而,随后getline()将丢弃换行符,而get()将换行符保留在输入序列中。
①面向行的输入:getline():使用方法:cin.getline(arryName,size),arryName时用来存储输入行的数组的名称,size是要读取的字符数。getline()成员函数在读取指定数目的字符或者遇到换行符时停止读取。
②面向行的输入:get():该函数有几种变体(函数重载),get()将换行符保留在输入序列中。如果需要两次cin输入,则正确的用法是:
cin.get(name1,size1);
cin.get(); // or cin.get(ch)读取一个换行符
cin.get(name2,size2);
另外一种使用这两个函数的方法是:
cin.get(name1,size1).get();
cin.getline(arryName,size).getline(name2,size2);之所以可以这样做,是由于cin.get(name1,size1)和cin.getline(arryName,size)返回一个cin对象;
总结:getline()使用起来简单一些,但get()使得检查错误更简单些;例如:假设用get()将一行读入数组中。如何知道停止读取的原因是由于已经读取了整行,而不是由于数组已填满?查看一下下一个输入字符,如果是换行符,说明已经读取了整行;否则,说明该行中还有其他输入。
3、sring类简介:要使用string类必须在程序中包含头文件string。string类位于名称空间std中,因此必须提供一条using编译指令,或者使用std::string来引用它。string 类定义隐藏了字符串的数组性质,能够像处理普通变量那样处理字符串。可以使用数组表示法来访问存储在string对象中的字符。注意:不能将一个数组赋给另一个数组,但可以将一个string对象赋给另一个string对象!例如:
char bird1[10];
char bird2[10]="Mr.Cheeps";
string str1;
string str2="panther";
bird1=bird2; // INVALID,no arry assignment
str1=str2; // VALID,object assignment ok
4、strcpy()\strcat()\strlen():函数由头文件cstring提供。简要说明:①strlen():计算字符串实际长度,不包括“\0”;②Strcat()将两个字符串连接起来组成一个字符串,如:char d[20]="GoldenGlobal"; char *s="View"; strcat(d,s);结果放在d中printf("%s",d);输出 d 为 GoldenGlobalView(中间无空格),d和s所指内存区域不可以重叠且d必须有足够的空间来容纳s的字符串。返回指向d的指针。③strcpy()将字符串复制到字符数组中。
char* strcpy(char *dest,char *src);将从src开始包含'\0'的字符串拷贝到以dest开始的位置,进行覆盖
char* strcat(char *dest,char *src);将src开始的字符串添加到dest字符串的末尾(覆盖dest的\0")
两者都返回指向dest的指针。
附:Strcpy()的内部实现:(复制包括strSrc最后的’\0’)
char *strcpy(char *strDest, const char *strSrc)
{
assert((strDest != NULL)&&(strSrc !=NULL));
char *tmp = strDest;
while ((*strDest++ = *strSrc++) !='\0')
{
;//或是 NULL;
}
return tmp;
}
5、由之前内容知道,将一行输入读取到数组中的代码为:cin.getline(arryName,size);将一行读取到string对象中的代码为:getline(cin,str);这里没有使用句点表示法,这表明这个getline()不是类方法!这里涉及到使用string类的友元函数!!
6、枚举的取值范围的定义:首先,要找出上限,需要知道枚举量的最大值。找到大于这个最大值、最小值的2的幂,将它减一,得到的值就是上限。例如:enum bigstep {first,secong=100,third};bigstep的最大值枚举值为101.在2的幂中,比这个数大的最小值为128,因此取值范围的上限为127.要计算下限,需要知道枚举量的最小值。如果它不小于0,则下限为0;否则, 采用与寻找上限方式相同的方式,但加上负号。例如,如果最小的枚举量为-6,而比它小的,最大的2的幂为-8,因此下限为-7.
7、一定要在对指针运用解除引用操作符(*)之前,将指针初始化为一个确定的,适当的地址。指针只是为可以通过名称直接访问的内存提供了一个别名。指针的真正用武之地在于,在运行阶段分配未命名的内存以存储值。在这种情况下,只能通过指针来访问内存。在C语言中,可以用库函数malloc()来分配内存;在C++中仍然可以这么做,但C++还有更好的方法——new操作符。
8、不要尝试释放已经释放的内存块,C++标准指出,这样做的结果将是不确定的,这意味着什么情况都可能发生。另外,不能使用delete释放声明变量所获得的内存。对空指针应用delete是安全的。
9、C++将数组名解释为数组的第一个元素的地址;
10、将指针变量加1后,其增加的值等于指向的类型占用的字节数。
11、使用数组表示法或者使用指针,则C++将执行同样的转换:
arryname[i] becomes *(arryname+i)或者pointername[i] becomes *(pointername+i);
注意:数组名和指针都表示地址。区别之一就是,可以修改指针的值,而数组名是常量;另一个区别就是,对数组应用sizeof操作符得到的是数组的长度;而对指针应用sizeof得到的是指针的长度,即使指针指向的是一个数组。
pointername =pointername+1 ;// valid
arryname = arryname +1; // not allowed
12、有如下代码:
char flower[10]="rose";
cout<<flower<<"s are red\n";
数组名是第一个元素的地址,因此cout语句中的flower是包含字符r的char元素的地址。cout对象认为char的地址是字符串的地址,因此它打印该地址处的字符,然后继续打印后面的字符,直到遇到空字符\0为止。。总之,如果给cout一个字符的地址,则它将从该字符开始打印,直到遇到空字符为止。注意:在C++中,用引号括起的字符串像数组名一样,也是第一个元素的地址。上述代码不会将整个字符串发送给cout,而只是发送该字符串的地址。这意味着对于数组中的字符串、用引号括起的字符串常量以及指针所描述的字符串,处理的方式一样,都将传递它们的地址。
记住:在cout和多数C++表达式中,char数组名、指向char的指针以及用引号括起的字符串常量都被解释为字符串第一个字符的地址。
13、一般来说,如果给cout提供一个指针,它将打印地址。但如果指针类型为char*,则cout将显示指向的字符串。如果要显示的是字符串的地址,则必须将这种指针强制转换为另一种指针类型,如int*
14、C++有三种管理数据内存的方式:
①自动存储:在函数内部定义的常规变量使用自动存储空间,被称为自动变量。实际上,自动变量是一个局部变量,其作用域为包含它的代码块。
②静态存储:静态存储是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字static。
③动态存储:由new和delete管理的一块内存池(自由存储空间)