前置:C语言
1.1 IO
1.1.1 cout 输出流
操作符 | 描述 |
---|---|
ws | 提取空白符 |
endl | 插入换行符并刷新流 |
ends | 插入空字符 |
setw(int) | 设置域宽 |
setprecision(int) | 设置浮点数小数位数(包含小数点) |
cout << setw(5) << setprecision(3) << 3.1415 << endl;
//设置域宽为5,小数点后保留两位有效小数
setw() 函数会用当前的填充字符控制对齐位置,默认的填充字符是空格
可以通过 <iomanip>
的 setfill
来设置填充字符
#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
cout << setfill('0') << setw(10) << 45698 << endl;//0000045698
return 0;
}
CPU所执行的指令并不对操作数的类型加以区分,对各个操作数都执行相同的操作,编译器根据变量的数据类型选择合适的指令
-
符号扩展:有符号数据类型,用符号位扩展
-
0扩展:无符号数据类型,用0填充
1.1.2 I/O格式化输出
cout
是 STL 库提供的一个 iostream
实例,拥有 ios_base
基类的全部函数和成员数据
进行格式化定义可以使用 setf
或 unsetf
函数和 flag
函数
cout
维护一个当前的格式状态
setf
和unsetf
是在当前的格式状态上追加或删除指定的格式
flag
则是将当前格式状态全部替换成指定的格式
操作符名 | 含义 |
---|---|
ios::dec | 数值类型采用十进制表示 |
ios::hex | 数值类型采用十六进制表示 |
ios::oct | 数值类型采用八进制表示 |
ios::showbase | 为整数添加一个表示其进制的前缀 |
ios::internal | 在符号位和数值中间按需插入字符,使两端对齐 |
ios::left | 在串的末尾插入填充字符使串左对齐 |
ios::right | 在串的末尾插入填充字符使串右对齐 |
ios::boolalpha | 将 bool 类型的值以 true 或 false 表示 |
ios::fixed | 将浮点数按照普通定点格式处理(非科学计数法) |
ios::scientific | 将浮点数按科学计数法处理(带指数域) |
ios::showpoint | 在浮点数表示的小数中强制插入小数点(默认情况下浮点数表示的整数不显示小数点) |
ios::showpos | 强制在正数前添加 + |
ios::skipws | 忽略前导空格,主要用于 cin |
ios::unitbuf | 在每次输出后清空缓存 |
ios::uppercase | 强制大写字母 |
以上每种格式都独立占用一bit,可以用 |
运算符组合使用,调用 setf/unsetf
设置输出格式一般用法
setf
可接受一个或两个参数
cout.setf(指定格式);
cout.setf(指定格式,删除的格式);
cout.setf(ios::right | ios::hex);//设置十六进制右对齐
cout.setf(ios::right ,ios::adjustfield);//取消其他对对齐,设置为右对齐
以预定义的bit组合位
ios::adjustifield
对齐格式的bit组合位ios::basefield
进制的bit组合位ios::floatfield
浮点表示方式的bit组合位
设置格式后,之后所有的 cout
都会按照指定的格式状态执行
如果在一次输出过程中需要混在多种格式,使用 cout
的成员函数来处理就很不方便了,STL
提供了一套 <iomanip>
库可以满足这种使用方式
<iomanip>
库中将每一种格式的设置和删除都进行了函数级的同名封装,比如 fixed
,就可以将一个ostream的对象作为参数,在内部调用 setf(ios::fixed)
后再返回原对象。此外 <iomanip>
还提供了setiosflags、setbase、setfill、setw、setprecision等方便的格式控制函数。大多数示例代码都会使用到,因此默认包含的头文件均为:
#include <iomanip>
#include <iostream>
缩进
将输出内容按指定的宽度对齐
#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
cout.flags(ios::left); //左对齐
cout << setw(10) << -456.98 << "The End" << endl;
cout.flags(ios::internal); //两端对齐
cout << setw(10) << -456.98 << "The End" << endl;
cout.flags(ios::right); //右对齐
cout << setw(10) << -456.98 << "The End" << endl;
return 0;
}
#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
cout << left << setw(10) << -456.98 << "The End" << endl; //左对齐
cout << internal << setw(10) << -456.98 << "The End" << endl; //两端对齐
cout << right << setw(10) << -456.98 << "The End" << endl; //右对齐
return 0;
}
整数
#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
cout.setf(ios::showpos | ios::uppercase);
cout << showbase << hex << setw(4) << 12 << setw(12) << -12 << endl;
cout << showbase << dec << setw(4) << 12 << setw(12) << -12 << endl;
cout << showbase << oct << setw(4) << 12 << setw(12) << -12 << endl;
cout.unsetf(ios::showpos | ios::uppercase);
cout << noshowbase << hex << setw(4) << 12 << setw(12) << -12 << endl;
cout << noshowbase << dec << setw(4) << 12 << setw(12) << -12 << endl;
cout << noshowbase << oct << setw(4) << 12 << setw(12) << -12 << endl;
return 0;
}
由于 <iomanip>
的 setbase()
函数设置整数进制反而比上述方法还复杂,所以除非特殊的代码规范要求,一般不建议使用
小数
分为两种:
- 定点表示
ios::fixed
,不带指数域 - 科学计数法表示
ios::scientific
带指数域
与 <iomanip>
的 setprecision
配合使用,可以指定小数点后保留位数
#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
cout.setf(ios::fixed);
cout << setprecision(0) << 12.05 << endl;
cout << setprecision(1) << 12.05 << endl;
cout << setprecision(2) << 12.05 << endl;
cout << setprecision(3) << 12.05 << endl;
cout.setf(ios::scientific, ios::floatfield);
cout << setprecision(0) << 12.05 << endl;
cout << setprecision(1) << 12.05 << endl;
cout << setprecision(2) << 12.05 << endl;
cout << setprecision(3) << 12.05 << endl;
return 0;
}
有时因为机器表示浮点数的舍入误差,需要手动修正
#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
cout << fixed << setprecision(1) << 2.05 << endl;//2.0
cout << fixed << setprecision(1) << 2.05 + 1e-8 << endl;//2.1
return 0;
}
字符串
字符串的输出处理主要是对齐,
字符串的输入方法
getline()
会读取屏幕上输入的字符,直至遇到换行符 \n
位置
getline(指定输入流,指定一个string对象)
getline(指定输入流,指定一个string对象,指定的结束字符)
getline
不会读入结束符号,读取指针会跳转至下一个非结束字符处
#include <iomanip>
#include <iostream>
#include <string>
using namespace std;
int main(void) {
string str1, str2;
getline(cin, str1);
cin >> str2;
cout << str1 << endl << str2 << endl;
return 0;
}
输入:
abc
abc
结果:
abc
abc
缓冲区
由于调用系统函数在屏幕上逐个显示字符是很慢的,因此cin/cout为了加快速度使用缓冲区技术,粗略的讲就是暂时不输出指定的字符,而是存放在缓冲区中,在合适的时机一次性输出到屏幕上
如果要和C标准库的stdio库函数混合使用就必须要小心的处理缓冲区了
如果要与scanf和printf联合使用,务必在调用cout前加上cout.sync_with_stdio()
,设置与stdio同步,否则输出的数据顺序会发生混乱。
flush和endl都会将当前缓冲区中的内容立即写入到屏幕上,而unitbuf/nounitbuf可以禁止或启用缓冲区。
#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
cout << 123 << flush << 456 << endl;
cout << unitbuf << 123 << nounitbuf << 456 << endl;
return 0;
}
1.2 函数
C++完全兼容了 C语言结构化程序设计
- 自顶向下:针对可能存在二义性的问题描述,转化为流程化、有限确定的算法描述,进而用无二义性的程序进行实现
- 按功能划分模块
- 每一模块都由顺序,选择,循环三种基本结构组成
- 模块化实现的具体方法为子程序
1.2.1 基本结构
/*求x的n次方*/
# include<iostream>
using namespace std;
double mypower(int x,int n){
double val = 1.0;
while(n--)
val *= x;
return val;
}
int main(){
int x,n;
cin >> x >> n;
cout << x << "的" << n << "次幂是:" << mypower(x,n) << endl;
return 0;
}
/*二进制转十进制*/
# include<iostream>
using namespace std;
/*x的n次幂*/
double power(int x,int n){
double val = 1.0;
while(n--)
val *= x;
return val;
}
int main(){
int val = 0;
char ch;
/*每8位为一组*/
for(int i = 8;i >= 0;--i){
cin >> ch;
if('1' == ch)
val += static_cast<int>(power(2,i));
}
return 0;
}
随机数
/*随机数的获取*/
# include<iostream>
# include<cstdlib>
using namespace std;
enum GameStatus{WIN,LOSE,PLAYING};
/*产生结果*/
int rollDice(){
int die1 = 1 + rand()%6;
int die2 = 1 + rand()%6;
cout << "payer rolled" << die1 << "+" << die2 << "=" << die1 + die2 << endl;
return die1 + die2;
}
int main(){
int sum,myPoint;
GameStatus status;
unsigned seed;
cin >> seed;//输入随机数种子,即为随机数序列设置初值,不同初值,产生的随机数序列不同
srand(seed);//产生随机数序列
sum = rollDice();
switch(sum){
case 7:
case 11:
status = WIN;
break;
case 2:
case 3:
case 12:
status = LOSE;
break;
default:
status = PLAYING;
myPoint = sum;
cout << "point is" << sum << endl;
}
while(PLAYING == status){
sum = rollDice();
if(sum == myPoint)
status == WIN;
else if(7 == sum)
status == LOSE;
}
if(WIN == status)
cout << "You win" << endl;
else
cout << "You lose" << endl;
return 0;
}
数学函数
s i n ( x ) = x + x 3 3 ! + x 5 5 ! + . . . + x 2 n − 1 ( 2 n − 1 ) ! \begin{aligned} sin(x) = x+\frac{x^3}{3!}+\frac{x^5}{5!}+...+\frac{x^{2n-1}}{(2n-1)!} \end{aligned} sin(x)=x+3!x3+5!x5+...+(2n−1)!x2n−1
# include<iostream>
# include<cmath>
using namespace std;
const double TINY_VALUE = 1e-10;
double tsin(double x){
double g = 0;
double t = x;
int n = 1;
do{
g += t;
n++;
t = -t*x*x/(2*n-1)/(2*n-1);
}while(fabs(t) >= TINY_VALUE);//涉及精确度,添加精度变量
return g;
}
1.2.2 递归
# include<iostream>
using namespacce std;
int c(int n,int k){
if(k > n)
return 0;
else if(n == k || k == 0)
return 1;
else
return c(n-1,k)+c(n-1,k-1);
}
int main(){
int n,k;
cin >> n >> k;
cout << "C(n,k)" << c(n,k) << endl;
return 0;
}
1.2.3 引用形参
一个变量的别名,指向同一块内存空间
声明引用时,必须对他初始化,使他指向已存在的对象
- 只能初始化,不能修改
在作为函数参数时:执行主调函数中的 调用 时才会为变量分配内存,同时用实参来初始化形参
1.2.4 内联函数
内联函数不在调用时发生控制转移,只是将代码嵌入到调用处
适用于功能简单,规模小,经常使用的函数
inline 数据类型 函数名(形参列表){
/*函数体*/
}
1.2.5 带默认形参的函数
在函数声明时,声明默认值
带默认形参值的形参必须在参数列表的最后
不允许对同于函数的形参多次定义,默认形参只能声明一次,声明时初始化,定义是不需要再次初始化的
1.2.6 函数重载
两个以上的函数,具有相同的函数名,但是形参个数或数据类型不同,编译器根据不同的参数列表调用最佳匹配函数
- 构造函数可以重载,析构函数不能重载
在C标准中
-
求整数的绝对值
abs()
和labs()
应该包含stdlib.h
-
求浮点数的绝对值
fabs()
应该包含math.h
在C++标准中
-
stdlib.h:
int abs(int n);
long int abs(long int n);
-
math.h:
double abs(int n);
float abs(float x);
long double abs(long double x);