C++ primer plus 第四章 复合类型 遗忘点速记
数组
- sizeof()用在数组名可以返回整个数组的字节数。
- 数组如果显式进行初始化,如
int cards[4] = {1,2};
则所有元素都被初始化,但只有前两个分别被指定为1,2,其它初始化为0;也就是说,显示初始化数组的时候,所有元素都被初始化,若指定了k个值,则数组前k个元素按指定的值进行初始化,其它都初始化为0;所以数组初始化为全0只需要将第一个初始化为0即可:
int cards[4] = {0};
int cards[] = {1,2};
则会自动根据花括号内的元素个数计算数组长度进行初始化,这里会被初始化为 cards[2] ,这一技巧在初始化字符串时比较有用
- c++11 提供了新的数组技巧,如
- 等号可以省略:
int cards[4] {1,2}; - 可以一个元素都不填,则全部初始化为0:
int cards[4] = {}; - 禁止缩窄转化,如不可将float变量作为整数数组的初始化元素
- 数组的size不要用变量赋值,但可以用const int赋值
字符串
- c-style string是末尾几个元素为’\0’的char数组
- ‘\0’并不是空格,空格是’ ',值为32,即0x20,而 ‘\0’ 值为0
- 用双引号声明初始化的char数组会自动在字符串末尾后全部初始化为0字符,若未指定数组长度,将自动初始化长度为字符个数加1,多出来的是末尾的’\0’,如
char cards[] = "aa";
这时sizeof(cards) 返回3
cout<<"aa" "bb"
"cc";
将输出的是aabbcc,空格/换行/制表符隔开的两个字符串都会直接拼成一个并且中间没有空格和换行,也没有0字符
- cstring.h中的strlen函数返回的是字符串的长度而非字符数组的长度,也就是说sizeof()一个字符数组返回的长度总是大于strlen()一个字符数组返回的长度
- cin只读取一个单词,使用空白(空格、制表符、换行符)来确定字符串的结束位置,将空白前的字符串读取到字符数组中,并自动在结尾添加空字符,比如,当程序如下:
char input[100];
cin >> input;
cout << input;
若输入为
hello world
则输出只有
hello
也即cin只读了 hello,剩下的 world 将会在同一程序的下一次 cin 被读取或根本不读取
- 如果想要读一行,即不以空格为分隔符而以回车为分隔符,可以用cin.getline()函数,使用方法如下
char input[100];
cin.getline(input, 40);
cout << input;
这里40的意思是,最多读取39个字符,并且在 input[39] 处填 ‘\0’ 。getline在读取到最大字符数时,或是读到换行符时会停下来,并丢弃换行符,也就是说getline是读不到换行符的。
- 另一种读一行的方法是 cin.get(input, 40),get函数与 getline 的区别是它不丢弃换行符,也就是说下一次还是会读到换行符并且停下来,永远绕不过去第一个换行符,除非用其它方法绕过缓冲区的换行符。
- 不带参数的 cin.get() 可以读取单个字符,可以用这种方法跳过换行符,即:
char input[100];
cin.get(input, 40).get();
cout << input;
这段代码和上一段代码等效。实际使用中建议用get而非getline,因为getline后无法显示得知停止的原因是读到上限还是读到换行符,而用get则可以检查下一个字符是否是换行符从而得知是否已经读取整行。
- 读取到空行后,会设置失效位并阻断后续输入,需要通过 cin.clear()来恢复,具体细节等后面再看,这一章不做介绍。
- 使用如下代码将在缓冲区中留下一个换行符,后续如果用getline读取输入很容易出错:
int year;
cin >> year;
!!注意!! 这里出错很容易找不到原因,要记住 cin >> 一个非字符/字符串的时候,是不会读入回车的,回车会被留在缓冲区,一般要通过cin.get()去掉!
string类
- string类也可以用getline进行读取一行的操作,但此时不再是使用cin的成员函数,而是一个普通的函数,如下:
getline(cin, mystr);
其它字符串
如下的五行代码分别表示w_char,char16_t,char32_t,UTF-8和原始字符串:
w_char title[] = L"aa bb";
char16_t name[] = u"aa bb";
char32_t car[] = U"aa bb";
char utf8[] = u8"aa bb";
char raw[] = R"(aa bb)";
关于原始字符串和UTF-8是什么,这里就不说了,有兴趣可以自己查
结构体
- C++在创建结构体变量时,可以省略struct,这点有别于C语言,如下:
struct myst
{
char name[20];
int point;
};
struct myst st1;
myst st1;
struct myst
{
char name[20];
int point;
};
myst st1 = {"aa bb", 100};
myst st2 = {};
上述的第二种初始化方式将 st2 的所有字节初始化为0
- 结构体可以进行赋值,会将所有元素赋值为等式右边的相同结构体的元素的值,若包含数组成员,也会将数组进行复制,这叫做“成员赋值”。
- 可以通过冒号来指定结构体的位字段,没有命名的位字段作为间距,如下:
struct torgle_register
{
unsigned int SN : 4;
unsigned int :4;
bool goodIn : 1;
bool goodTorgle : 1;
};
这样声明的结构体,前4个bit作为SN的存储,然后中间留4个bit的空隙,后面每个bool变量占1个bit,该结构体一共占10个 bit。
共用体
- 将struct替换成union可以声明共用体,里面所有的成员共享内存。
- 若在结构体内包含未命名的匿名共用体,共用体的成员变为结构体的成员,可以直接用结构体.成员名获取。该使用方法可以在特定情况下节省内存,比如当结构体某两个成员在各种情况下只有其中一个有效时,就可以声明一个共用体被这两个成员共用,如下:
struct myst
{
bool type;
union
{
float float_id;
int int_id;
};
}
myst.type = true;
myst.float_id = 1.23;
枚举
enum spectrum {red, orange, yellow, green};
spectrum band = red;
- 当然枚举还有很多骚操作,比如显式设置枚举量的值等,但是这些操作不常见,就不管了。
指针
- 可以认为,声明指针时,* 号是用来修饰变量名的,因此如果是下面这个句子,second 是一个 int 变量而非一个 int 指针,如果要声明多个指针,每个前面都要加 * :
int *first, second;
- 但即使是这样,声明指针时,初始化的对象仍然是指针,如下:
int higgens = 5;
int *pt = &higgens;
- new的使用方法:new 返回的是分配的内存的地址
int *pt = new int;
int *myarray = new int[10];
delete pt;
delete [] myarray;
- 指针变量加1后,指针的值增加的值等于其指向的类型占用的字节数。
- 数组名和指针其实还是有很大区别的,虽然多数情况下可以把数组名当作其首个元素的指针,但其实他们的类型还是有所差别,体现在:
- 数组名是常量,不能修改,而指针可以是变量,可以被赋值和修改;
- sizeof 数组名会返回数组的长度,而sizeof指针仅仅返回指针的值所占的字节数,即指针的长度,而无论指针是否指向一个数组。
- 对数组名取地址会发生什么呢?看下面代码:
int tell[10];
cout << tell << endl;
cout << &tell[0] << endl;
cout << &tell << endl;
从数字上说,上面三个的值都相同,会输出三个相同的数字,但是概念上说,&tell[0] (即 tell ),指向的是一个4字节内存块的地址,而&tell 指向的是一个40字节内存块的地址。再进一步说,tell + 1 会将地址值增加4,而&tell +1 将地址值增加40。这样大概就明白了,tell 是一个int * ,而&tell 是一个int (*)[10] ,也就是说,有关&tell 的指针,应该这样声明和初始化:
int (*pt)[10] = &tell;
注意这里的括号不可以去掉,去掉括号就变成了int * 的数组了 。
- 给cout提供一个普通指针,他将打印指针的值,也即它指向的地址,但如果提供一个char *,它将打印以该指针指向的char 字符为首的一串字符串,直至遇到 ‘\0’ 为止。
- 用vector可以代替动态数组,用array(c++11)可以代替静态数组。vector用得多,array了解得比较少,用法如下:
#include<array>
array<double, 10> myarray;
array与静态数组的区别在于,可以直接用赋值语句进行复制,如下:
#include<array>
array<double, 3> myarray = {1.2, 3.0, 5.4};
array<double, 3> myarray2;
myarray2 = myarray;
这时myarray2将逐元素对myarray进行复制。但是,与静态数组一样,array<typeName, n_elem> 的n_elem不允许使用变量,这点与动态数组和vector区别:
#include<vector>
int n = 3;
vector<double> myvector(n);
|