目录
第四章. 复合类型
5. 共用体
用法跟结构一样,区别主要是,结构中所有元素都会分配内存,而共用体(联合)是所有元素共用一块内存,即一次只能表示一种类型,大小为最大类型的大小。
union one4all // 大小是long的长度
{
int int_val;
long long_val;
} pail;
pail.int_val = 15;
6. 枚举
枚举内定义的都是常量,声明一个枚举变量后,该变量的值只能是枚举范围内的值,枚举内定义的常量可以自动转换为int,但int不能自动转换为枚举类型。枚举类型没有定义算数运算,在表达式中出现算数运算,自动将枚举常量转为int。
enum spetrum {red, orange, yellow, green, blue,
violet, indigo, ultraviolet}; // red-ultra的值为0-7
spetrum band;
band = orange; //有效
band++; //无效
band = red + green; // 无效,red、green自动转换为int,而int无法转换为枚举,从而无法为band赋值
int color = red; //red被自动转换成int,color = 0
band = 3; // 无效,int不能自动转换为枚举
band = spectrum(5); //有效,5在枚举范围内
枚举常量的值可以初始化,值必须是整数,没初始化的值比上一个大1,枚举常量的值可以相同。枚举的范围是大于最大值的2的最小次幂减一,如果枚举最小值大于0,则下限是0,否则按照上限的方式求下限。但是若int类型常量在枚举范围内,可以强制转换为枚举。
enum bits {one = 1, null = 1, two = 100, three}; // three = 101
// bits 的取值范围是0-127,大于101的2的最小次幂是128,下限是0
// 如果one = -6,则下限是小于-6的2的最小次幂-8,加1,下限为-7
bits num = bits(111); // 有效,111在bits范围内
7.指针和自由储存空间
未初始化的指针不要指向常量,未初始化的指针指向哪未知,该常量储存的内存位置也就不确定,可能产生bug。要将数字值作为地址赋给指针要强制转换。
int *pt;
pt = 0xB8000000; // 无效,0XB8000000是整型
pt = (int*)0xB8000000; //有效
*pt = 200; //不要这样做
指针变量+1时,增加的量是它指向类型的字节数,如上面代码中pt+1的值为0xB8000004(看系统)。数组名可以作为指针常量,即数组名也就是该数组的数组指针,不过数组指针和数组首元素的地址相同。
int num[4];
int *pt = num;
num = num + 1; //无效,数组指针是常量
pt = pt + 1; //有效,地址值加4
而对数组名用 地址运算符 & 时,会被解释为整个数组的地址,&num+1的值要加16字节,而pt+1的值加4字节。
8. new运算符
typeName * pointerName = new typeName;
delete pointerName;
用new分配一段内存空间,new分配的内存是未使用的,不会产生bug。delete释放完new后,并没有删除new的指针,该指针可以继续指向其他同类型的变量。
typeName * pointerName = new typeName [num_elements];
delete [] pointerName;
new创建动态数组时要告知元素数量,delete时不要忘了[](中括号是空的)。使用的时候跟数组的使用方式一样,指针指向第一个元素。
9. 数组的替代品
#include <vector>
using namespace std; // 或者通过std :: vector<int> vi使用vector
// vector<typeName> vt(n_elem) n_elem可以说变量,也可以是常量
vector <int> vi;
要使用vector类,要包含vector头文件,它在std命名空间中。它可以在使用中调整数组长度,因此可以不设置长度。
#include <array>
using namespace std; // 或者通过std :: array<int, 5> ai使用array
array<int, 5> ai = {1, 2, 3};
//array<typeName, n_elem> arr n_elem只能是常量
array类是固定长度的数组,要使用也需要头文件和命名空间std。
第5章. 循环和关系表达式
1. for 循环
赋值表达式的值定义为左侧成员的值。
y = (x = 14); // y的值是14
前缀递增、减和解引用运算符优先级相同,按从右到左的方式结合,后缀递增、减优先级高于解引用。
int *pt = NULL;
int a[2] = {1, 2};
pt = a;
int m = *++pt; // pt指针先递增后解引用,m = 2;
--pt;
int n = *pt++; // 因为后缀作用,先赋值,后pt指针递增,而不是值。
// n = 1, pt指向a[1]
在循环块中定义的变量会隐藏外部的同名变量,在块外,外部变量再重新使用。
int x = 10;
for (int i = 0; i < 3; i++)
{
cout << x << endl; // x = 10
int x = 20;
cout << x << endl; // x = 20
}
cout << x << endl; // x = 10
逗号运算符使得多条语句放在 C++ 句法只能放一条语句的地方。逗号运算符保证从左到右的按顺序计算,即先计算第一个表达式。逗号表达式的值是最后部分的值。
int n = (1, 2, 3);
cout << n << endl; // n = 3
for 循环中的三个表达式都可以省略,省略测试表达式时循环会一直进行下去。
字符串数组名与字符串常量本身都表示它们的地址。
char ch[] = "go to sleep";
ch == "mate"; // 比较二者地址是否相同
strcmp(str, str2)函数比较两字符串大小(输入指针),按字符逐字比较(ASCII码),前者比后者小,函数值为负数,两字符串相同,函数值为0,否则为正数。
string类字符串可以直接使用关系符比较字符串。
2. while 循环
clock() 函数返回一个 long 或其他类型的值,单位不定,可能是秒。头文件ctime定义了一个符号常量CLOCK_PER_SEC ,它指系统时间单位为单位的时间,CLOCK_PER_SEC乘以 clock() 的返回值,可以得到秒数,并定义了clock_t 为 clock() 的返回类型的别名。
类型别名有两种方式,#define 预处理器以及关键字 typedef,预处理器方式可能会出问题。
#define CHAR_POINTER char *
typedef char * pt;
pt p1, p2; // p1, p2为pt类型变量,pt是 char 指针类型,所以p1 p2都是char指针
CHAR_POINTER p3, p4; // 相当于char * p3, p4, p4是字符变量,不是指针
3. do while 循环
至少执行循环体一次。
4. 基于范围的 for 循环
该方法是C++11新特性,对数组或容器类的每个元素执行相同操作,若要对数组值进行修改,使用引用。
double prices[3] = {4.8, 10.4, 7.99}
for (double x : prices) // 依次打印prices每个元素
cout << x << endl;
for (int x : {1, 2, 3})
cout << x << ' '; // 依次打印1 2 3
for (double &x : prices)
x *= 0.8; // prices每个元素缩小0.8倍
5. 循环和文本输入
cin 会忽略空格和换行符,从第一个字符开始,在输入回车时才会发送给程序,之前字符在缓冲区。C++函数重载允许多个同名函数,只要函数参数列表不同就行,如 cin.get() ,可以有两个参数、一个参数或没有参数,这是三个不同的同名函数。cin.fail() 和 cin.eof() 函数检查输入是否是 EOF(ctrl+z和enter(windows下)),是的话,都返回true。
cin.get(ch) 返回一个 cin 对象,istream 类提供了函数将 istream 对象(如 cin)转换成 bool 类型(cin 需要为 bool 时自动调用),可以以此判断输入是否是EOF。
while (cin.get(ch)) // 输入的ch是否是EOF,是eof,函数返回false
cin.get(无参数)返回char类型。
6. 嵌套循环和二维数组
初始化二维数组时,全用逗号。
int buts[2][2] = {(1, 2,), (3, 4)}; // 都是逗号