一、C++编程基础(2)

本文介绍了C++中的运算符种类,包括算术、关系、逻辑和复合赋值运算符,以及条件运算符的使用。详细讲解了循环控制结构,如for、while和switch,并探讨了指针、数组、vector及其操作。还涉及了文件的读写操作,包括fstream库的使用。文章强调了理解运算优先级和正确使用指针的重要性,以及在处理数组和vector时避免off-by-one错误的注意事项。
  • 运算符分为算术运算符、关系运算符、逻辑运算符、复合赋值运算符。
  • 两个整数相除,小数点之后的部分会被完全舍弃。并没有四舍五入。
  • 条件运算符的一般使用形式如下:
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
  • arrayvector都可以指定容器中的某个位置,进而访问该位置上的元素。索引操作(index)通过下标运算符[]达成。

注:容器的第一个元素位置为0而非1。容易引起 off-by-one 错误。

  • for循环包含以下几个组成部分:
for( init-statement ; condition ; expression )
	statement //单一语句或语句块

init-statement在循环开始前被执行一次;condition用于循环控制,每次循环迭代之前被计算出来,为真则执行statementexpression会在每次循环迭代结束之后被求值,通常用来改变在init-statement中被初始化的对象和在condition中被检验的对象。

  • 如果condition第一次求值即为false,那么expression不会被执行。
  • 可以在init-statementexpression甚至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。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值