- 运算符分为算术运算符、关系运算符、逻辑运算符、复合赋值运算符。
- 两个整数相除,小数点之后的部分会被完全舍弃。并没有四舍五入。
- 条件运算符的一般使用形式如下:
expr
? //如果expr为true,执行这里
: //如果expr为false,则执行这里
- 条件表达式的值如果为0,会被视为
false,其他非零值被视为true。
//如果余数运算结果为0,则条件运算的结果为'\n'
//如果余数运算的结果非0,则条件运算结果为' '
cout << a_string
<< (cnt % line_size ? ' ' : '\n');
- 复合赋值(compound assignment)运算符可以和每个算术运算符结合,形成
+=、-=、*=、/=和%=。 - 递增运算符
++和递减运算符--都有前置和后置两种形式。
int tries = 0;
cout << ++tries; //输出1,tries变为1
cout << tries++; //输出1,tries变为2
- 任何一个关系运算符(relation operator)
==、!=、<、>、<=、>=的求值结果不是true就是false。 - 逻辑运算符OR
||、AND&&、NOT!。 - 运算优先级是C++编程之所以复杂的原因之一。
优先级从上到下越来越低。同一行优先级相同,求值次序取决于表达式中的位置(从左至右)。
逻辑运算符NOT
算术运算符*、/、%
算术运算符+、-
关系运算符<、>、<=、>=
关系运算符==、!=
逻辑运算符AND
逻辑运算符OR
赋值运算符=
- 执行多条语句时,以大括号将这些语句括住,这样便称为一个语句块。
- 关键字
switch之后紧接一个由小括号括住的表达式,该表达式的值必须是整数形式。整数之后是一组case标签,每个标签后都指定一个常量表达式。在switch之后的表达式被计算出来后,依次与每个case标签的表达式值相比较。若找到相符的case标签,便执行该标签之后的语句(这个操作会一直执行到switch语句的最下面,除非我们用break来结束执行,这也称为“向下穿越”);若找不到但有default标签,便执行default之后的语句;若没有default标签,就不执行任何操作。 - “向下穿越”行为模式的合理性如下:
switch(next_char)//对象名称也可视为表达式
{
case 'a':case 'A':
case 'e':case 'E':
case 'i':case 'I':
case 'o':case 'O':
case 'u':case 'U':
++vowel_cnt;//不用一直重复对元音字母计数
break;
//...
}
- 如果在执行循环内的语句时遇上
break语句,循环便会结束。我们也可以利用continue语句来终止循环的当前迭代(current iteration)。
while(tries_cnt < max_tries)
{
if(tries_cnt == 1)
{
continue;
//结束当前迭代,循环中的剩余部分不会被执行
//循环重新来过,而条件表达式tries_cnt < max_tries会被再一次求值
}
if(usr_guess == next_elem)
break;//结束循环
tries_cnt++;
//...
}
- 容器(container)可存放连续值,不仅可以用名称(name)取用容器中的元素,也可以用容器中的位置来取用元素。C++允许我们以内置的
array(数组)类型或标准库提供的vector类来定义容器。 array的定义需要指定元素类型、对象名称并指定尺度大小(即所能储存的元素个数)。array的大小必须是个常量表达式(constant expression),不需要在运行时求值。
const int seq_size = 18;
int pell_seq[ seq_size ];//seq_size是一个常量表达式
vector的定义必须包含vector头文件#include <vector>。vector是一个模板类(template class),要在类名后的尖括号内指定其元素类型,大小则写在小括号中(大小不一定是常量表达式)。
#include <veector>
vector<int> pell_seq( seq_size );
//vector对象pell_seq,可储存18个int元素,每个元素初值为0
array和vector都可以指定容器中的某个位置,进而访问该位置上的元素。索引操作(index)通过下标运算符[]达成。
注:容器的第一个元素位置为0而非1。容易引起 off-by-one 错误。
- for循环包含以下几个组成部分:
for( init-statement ; condition ; expression )
statement //单一语句或语句块
init-statement在循环开始前被执行一次;condition用于循环控制,每次循环迭代之前被计算出来,为真则执行statement;expression会在每次循环迭代结束之后被求值,通常用来改变在init-statement中被初始化的对象和在condition中被检验的对象。
- 如果
condition第一次求值即为false,那么expression不会被执行。 - 可以在
init-statement或expression甚至condition处(较罕见)留空。 - 初始化列表内的元素个数不能超过
array的大小。当元素个数小于array的大小时,其余的元素值会被初始化为0。我们也可以让编译器根据初值的数量自行计算出array的大小,如:
//编译器会算出此array包含了18个元素
int elem_seq[] = {
1, 2, 3, 3, 4, 7, 2, 5, 12,
3, 6, 10, 4, 9, 16, 5, 12, 22
};
vector不支持上述初始化列表,可以为每个元素指定值,或利用一个已初始化的array作为该vector的初值:
int elem_vals[ seq_size ] = {
1, 2, 3, 3, 4, 7, 2, 5, 12,
3, 6, 10, 4, 9, 16, 5, 12, 22
};
vector<int> elem_seq (elem_vals, elem_vals + seq_size );
//传入elem_seq的两个值都是实际内存位置,标识出用以将vector初始化的元素范围
- 指针(pointer)为程序引入了一层间接性,舍弃以名称指定的方式,间接地访问每个vector,借此达到透明化的目的。指针代表某特定内存地址,而不再直接操作对象。
- 指针增加程序本身弹性的同时,也增加了直接操作对象时所没有的复杂度。
- 当我们要定义某个特定类型的指针时,必须在类型名称之后加上
*号。指针的形式为:
type_of_object_pointed_to * name_of_pointer_object
- 如果希望取得对象所在的内存地址而非对象的值,则应该使用取址运算符
&。 - 提领(dereference)操作用来访问一个由指针所指的对象,也就是取得位于该指针所指内存地址上的对象。在指针之前使用
*号即可。
int ival = 1024;
int *pi = &ival;//将pi的初值设为ival所在的内存地址
pi;//计算pi所持有的内存地址
*pi;//求ival的值
- 一个未指向任何对象的指针,其地址值为0。有时候我们称为null指针。任何指针都可以被初始化,或是令其值为0(此举可以防止对未指向任何对象的指针做提领)。
//初始化每一个指针,使他们不指向任何对象
int *pi = 0;
double *pd = 0;
string *ps = 0;
//校验指针所持有的地址是否为0
if( pi && *pi != 1024 )
*pi = 1024;
rand()和srand()是C语言标准库提供的伪随机数生成器。srand()的参数是随机数生成器的种子,每次调用rand()都会返回一个介于0和“int所能表示的最大整数”之间的一个整数。两个函数的声明位于cstdlib头文件。
#include <cstdlib>
srand(6);
index = rand();
- dot成员选择运算符(member selection operator)就是
fibonacci.empty()中的句点。而指针必须要改用arrow成员选择运算符pv -> empty()。 - 下标运算符的优先级较高,指针的提领操作两旁必须加上小括号:
if(pv && (*pv)[1] == 1)
//...
- 文件的读写操作要包含fstream头文件
#include <fstream>。 - 为了打开一个可供输出的文件,我们定义一个
ofstream(供输出的file stream)对象,并将文件名传入ofstream outfile("文件名");。如果指定的文件不存在,便会有一个文件被产生出来并打开供输出使用;如果指定的文件已经存在,这个文件会被打开用于输出,而文件中原有的数据会被丢弃。 - 如果不希望丢弃原有内容而是将新数据增加到该文件中,就以追加模式(append mode)打开这个文件:
ofstream outfile( "文件名", ios_base::app );
cerr代表标准错误设备(standard error)。和cout一样,cerr将输出结果定向到用户的终端。两者的差别是——cerr的输出结果并无缓冲情形,它会立即显示到用户终端中。
if( !outfile )
//因为某种原因,档案无法开启
cerr << "Oops!Unable to save session data!\n";
else
//开启数据写入
//将输出信息定向到文件中,就像写入cerr和cout一样
outfile << usr_name << ' '
<< num_tries << ' '
<< num_right << endl;
//endl是事先定义好的操纵符(manipulator)
- 操纵符由iostream library提供,并不会将数据写到iostream,也不会从中读取数据,只是在iostream上执行某些操作。
endl会插入一个换行符,并清除输出缓冲区的内容。
hex以十六进制显示整数。
oct以八进制显示整数。
setprecision(n)设定浮点数显示精度为n。
cout << hex << 100;//输出64
- 打开一个可供读取的文件,就定义一个
ifstream(供输入的file stream)对象。
ifstream infile("...")
infile >> name;//将infile的内容读入对象
- 想要同时读写同一个文件,我们要定义一个
fstream对象,并以追加模式打开。此时文件位置会位于末尾,如果直接读取会导致“文件结束”,seekg()可将iofile重新定位至文件的起始处;任何写操作都会将数据添加到文件末尾。
fstream iofile("...", ios_base::in|ios_base::app);
if( ! iofile )
//Oops!
else
{
//开始读取之前,将文件重定位至起始处
iofile.seekg( 0 );
//...
}
- string对象和C-style字符串之间的差异:
- string对象可以随字符串长度动态的增加储存空间,C-style字符串只能分配固定的空间;
- C-style字符串不记录自身长度,需要遍历每个元素,直至null字符出现,
strlen()就用于做这样的事(cstring头文件中)。
- array和vector的主要差异:
- array的大小必须固定,vector可以动态地随着元素的插入而扩展储存空间;
- array并不储存自身的大小,访问时要考虑溢出。且array并没有像null字符这样的“标兵”用来表示已到达末端。
- 要先读入再写入文件时,最好先将输入与输出的文件都打开,因为若是因某种原因无法打开输出文件,所有的计算都会付诸东流。
- 读文件时,ifstream对象相当于cin;写文件时,ofstream文件相当于cout。
本文介绍了C++中的运算符种类,包括算术、关系、逻辑和复合赋值运算符,以及条件运算符的使用。详细讲解了循环控制结构,如for、while和switch,并探讨了指针、数组、vector及其操作。还涉及了文件的读写操作,包括fstream库的使用。文章强调了理解运算优先级和正确使用指针的重要性,以及在处理数组和vector时避免off-by-one错误的注意事项。

被折叠的 条评论
为什么被折叠?



