——输出二进制、十进制、八进制和十六进制总结


在C++中,默认状态下,数据按十进制输入输出。如果要求按八进制或十六进制输入输出,在cin或cout中必须指明相应的数据形式
oct 为八进制,hex 为十六进制,dec 为十进制。但是二进制没有默认的输出格式,需要自己写函数进行转换。
#include <iostream>
#include <bitset>
 
using namespace std;
 
int main(void)
{
	int i,j,k,l;
	cout<<"Input i(oct),j(hex),k(hex),l(dec):"<<endl;
	cin>>oct>>i;     //输入为八进制数
	cin>>hex>>j;     //输入为十六进制数
	cin>>k;         //输入仍为十六进制数 在cin或cout中指明数制后,该数制将一直有效,直到重新指明使用其他数制 cin>>dec>>l;       //输入为十进制数
	cout<<"hex:"<<"i = "<<hex<<i<<endl;
	cout<<"dec:"<<"j = "<<dec<<j<<'\t'<<"k = "<<k<<endl;
	cout<<"oct:"<<"l = "<<oct<<l;
	cout<<dec<<endl;  //恢复十进制数输出状态
	return (0);
}
调试运行


思考与提示
  • 在接收输入时,必须在cin中指明数制,否则从键盘输入时,不认八进制和十六进制数开头的0和0x标志。指明后可省略0和0x标志。
  • 进制控制只适用于整型变量,不适用于实型和字符型变量。
  • 输入数据的格式、个数和类型必须与cin中的变量一一对应,否则不仅使输入数据错误,而且影响后面其他数据的正确输入。
  • 在cin或cout中指明数制后,该数制将一直有效,直到重新指明使用其他数制。

C++中二进制输出的总结
#include <iostream>
#include <list>
#include <bitset>
#include <iomanip>
 
using namespace std;
 
//递归输出二进制函数
void BinaryRecursion(int n)
{
	int a;
	a = n % 2;     // 取余
	n = n >> 1;   //右移一位 相当于除以2
	if(0 != n)
	{
		BinaryRecursion(n);
	}
	cout<<a;
}
 
//使用容器转换二进制
void BinaryVector(int n)
{
	int temp;
	temp = n;
	list <int> L;
	while(0 != temp)
	{
		L.push_front(temp % 2);
		temp = temp >> 1;
	}
 
	for(list <int>::iterator iter = L.begin(); iter != L.end(); iter++)
	{
		cout<<*iter;
	}
	cout <<endl;
}
 
//一般方法,32位,逐步与1做与运算
void Binarycout(int n)
{
	for(int i = 31; i>= 0; i--)
	{
		cout<<((n>>i)&1);
	}
 
	cout<<endl;
}
 
//使用bitset转换二进制
void BinaryBitset(int n)
{
	cout<<bitset<sizeof(int)*8>(n)<<endl;
}
 
int main()
{
	int a = 1045, b = 2;
	int c;
	c = a + b;
	cout<<setw(20)<<"BinaryRecursion("<<c<<"):";
	BinaryRecursion(c);
	cout<<endl;
 
	cout<<setw(20)<<"BinaryVector("<<c<<"):";
	BinaryVector(c);
 
	cout<<setw(20)<<"Binarycout("<<c<<"):";
	Binarycout(c);
 
	cout<<setw(20)<<"BinaryBitset("<<c<<"):";
	BinaryBitset(c);
 
	return (0);
}
调试运行


思考与提示
//递归输出二进制函数
void BinaryRecursion(int n)
{
	int a;
	a = n % 2; // 取余
	n = n >> 1;  //右移一位 相当于除以2
	if(0 != n)
	{
		BinaryRecursion(n);
	}
	cout<<a;
}
递归层次

使用递归的代价是十分巨大的:它会消耗大量的内存!!递归循环时它用的是堆栈,而堆栈的资源是十分有限的。假设调用该递归函数的主函数为0,则从主函数调用递归函数进入1;从第i层递归调用本身为进入“下一层”,即i+1。反之,退出第i层递归应返回至“上一层”,即i-1
为了保证递归函数正确执行,系统需设立一个“递归工作栈”作为整个递归函数运行期间使用的数据存储区。每一层递归所需信息构成一个“工作记录”,其中包括所有的实参、所有的局部变量以及上一层的返回地址。
每进入一层递归,就产生一个新的工作记录压入栈顶。每退出一层递归就从栈顶弹出一个工作记录,则当前执行层的工作记录必须是递归工作栈栈顶的工作记录,称这个记录为“活动记录”,并称指示活动记录的栈顶指针为“当前环境指针”。
如上图所示:当n 不等于0时,保存当前层的“工作记录”,然后递归调用进入下一层,直到n 等于0 ,此时是第四层,把当前层的a值 打印出来,然后退出第4层递归,返回至“上一层”即第4 – 1 层,即第3层。然后打印此层的a值 0,依次递归返回打印其余层。最后得到结果为 1010.



——输入输出格式控制

~输入输出宽度控制

setw(int n): 设置输入输出宽度,等价于io.width(n)
头文件: #include<iomanip>
cout<< setw(10) << 123 << setw(10) << 456 <<endl
//结果:
     123     456

~浮点数输出格式控制

resetionsflags:  为默认设置,浮点数按浮点格式输出
fixed:                  浮点数按定点格式输出,保留小数点后6位
scientific:           浮点数按指数格式(科学计数法)输出
cout<<314.15926535<<endl;
cout<<fixed<<314.15926535<<endl;
cout<<scientific<<314.15926535<<endl;
//结果:
314.159
314.159265
3.141593e+002

~输出精度控制

setprecision(int n):设置浮点数的精度(有效位数或小数位数)
setprecision(n) 指定一个浮点数的精度默认设置输出的数字的 总位数 为n包含整数和小数部分;其中setprecision(0)效果是跟c++默认的流输出数值一样,有效位是6位,包括整数和小数
fixed必须与setprecision(n)配合使用,用来控制小数位数,不够补0,只要写一次fixed,后面的setprecision(n)就都是指小数了。 fixed与setprecision谁先谁后没有关系,但通常是fixed在前先固定6位小数(若此时小数已经超出6位,则先四舍五入到6位)再precision(n)取n位小数(n<6)
#include <iostream>
#include <iomanip>
using namespace std;
{
double f = 3.123456789;

cout<<f<<endl;                         //输出3.12346 (包含整数和小数,且四舍五入)
cout<<setprecision(2)<<f<<endl;        //输出3.1(包含整数和小数,共两位,且最后一位四舍五入),这条会作用到下一条去
cout<<fixed<<f<<endl;                  //输出3.123457 (仅包含小数,且四舍五入),没有上一条,则输出六位小数3.123457
cout<<setprecision(2)<<fixed<<f<<endl;  //输出3.12  (小数2位,四舍五入)
cout<<fixed<<setprecision(2)<<f<<endl;  //效果同上

system("pause");
return 0;
}
  1. 如果与setiosnags(ios::scientific)合用, 可以控制指数表示法的小数位数。setiosflags(ios::scientific)是用指数方式表示实数。
  2. resetiosflags(ios::fixed) 取消精度的设置。
         1.)超出的位数会被四舍五入进去!!!
          2)与setw()不同setprecision(n)一直作用到下一个setprecisin(n)之前,所以,只需要写一个setprecision(n)就可以。setw()要每次都写


~对齐方式控制

left:          在设定的宽度内左对齐输出,右端使用设定的填充字符填充
right:       为默认设置,在设定宽度内右对齐输出,左端使用设定的填充字符填满
internal:  在设定的宽度内右对齐输出,但若有符号(-,+),符号至于最左端
double s = 12;
cout << setw(10) << s << "OK" << endl;
cout << left<< setw(10)<<s<< "OK" << endl;
//结果:
          12OK
12       OK


~小数点处理方式的控制

showpoint:       即使小数部分为0,也输出小数点及其后的无效0
noshowpoint:  为默认设置,小数部分的最后的0不输出
cout<<12.0<<endl;
cout<<showpoint<<12.0<<endl;
//结果
12
12.0000

~填充字符控制

setfill(char c):设置填充字符
cout<<setw(10)<<12<<endl;
cout<<setfill('*')<<setw(10)<<12<<endl;
//结果:
     12
********12



——字符&字符串输入输出结束控制


~getchar( ) & EOF

1)getchar的两点总结:


<1>getchar是以行为单位进行存取的。
        当用getchar进行输入时,如果输入的第一个字符为有效字符(即输入是文件结束符EOF,Windows下为组合键Ctrl+Z,Unix/Linux下为组合键Ctrl+D),那么只有当最后一个输入字符为换行符´\n´(也可以是文件结束符EOF,EOF将在后面讨论)时,getchar才会停止执行,整个程序将会往下执行。
while((c = getchar()) != EOF)
{
    putchar(c);
}
对于getchar,肯定很多初学的朋友会问,getchar不是以字符为单位读取的吗?那么,既然我输入了第一个字符a,肯定满足while循环(c = getchar()) != EOF的条件阿,那么应该执行putchar(c)在终端输出一个字符a。不错,我在用getchar的时候也是一直这么想的,但是程序就偏偏不着样执行,而是 必需读到一个换行符或者文件结束符EOF才进行一次输出。

解释是:  在大师编写C的时候,当时并没有所谓终端输入的概念,所有的输入实际上都是按照文件进行读取的,文件中一般都是以行为单位的。因此,只有遇到换行符,那么程序会认为输入结束,然后采取执行程序的其他部分。同时,输入是按照文件的方式存取的,那么要结束一个文件的输入就需用到EOF(Enf Of File). 这也就是为什么getchar结束输入退出时要用EOF的原因。


<2>getchar( )的返回值一般情况下是字符,但也可能是负值,即返回EOF
         
这里要强调的一点就是,getchar函数通常返回终端所输入的字符,这些字符系统中对应的ASCII值都是非负的。因此,很多时候,我们会写这样的两行代码:
char c;
c = getchar();
这样就很有可能出现问题因为getchar函数除了返回终端输入的字符外,在遇到Ctrl+D(Linux)即文件结束符EOF时,getchar()的返回EOF,这个EOF在函数库里一般定义为-1。因此,在这种情况下,getchar函数返回一个负值,把一个负值赋给一个char型的变量是不正确的。
为了能够让所定义的变量能够包含getchar函数返回的所有可能的值,正确的定义方法如下(K&R C中特别提到了这个问题)
int c;
c = getchar();


2)EOF的两点总结


<1>EOF作为文件结束符时的情况:
EOF虽然是文件结束符,但并不是在任何情况下输入Ctrl+D(Windows下Ctrl+Z)都能够实现文件结束的功能,只有在下列的条件下,才作为文件结束符。
  1. 遇到getcahr函数执行时,要输入第一个字符时就直接输入Ctrl+D,就可以跳出getchar(),去执行程序的其他部分;
  2. 在前面输入的字符为换行符时,接着输入Ctrl+D;
  3. 在前面有字符输入且不为换行符时,要连着输入两次Ctrl+D,这时第二次输入的Ctrl+D起到文件结束符的功能,至于第一次的Ctrl+D的作用将在下面介绍。
  其实,这三种情况都可以总结为只有在getchar()提示新的一次输入时,直接输入Ctrl+D才相当于文件结束符。


<2>EOF作为行结束符时的情况,这时候输入Ctrl+D并不能结束getchar(),而只能引发getchar()提示下一轮的输入。
这种情况主要是在进行getchar()新的一行输入时,当输入了若干字符(不能包含换行符)之后,直接输入Ctrl+D,此时的Ctrl+D并不是文件结束符,而只是相当于换行符的功能,即结束当前的输入。以上面的代码段为例,如果执行时输入abc,然后Ctrl+D,程序输出结果为:
abcabc
 注意:第一组abc为从终端输入的,然后输入Ctrl+D,就输出第二组abc,同时光标停在第二组字符的c后面,然后可以进行新一次的输入。这时如果再次输入Ctrl+D,则起到了文件结束符的作用,结束getchar()。

如果输入abc之后,然后回车,输入换行符的话,则终端显示为:
abc     //第一行,带回车
abc     //第二行
           //第三行
其中第一行为终端输入,第二行为终端输出,光标停在了第三行处,等待新一次的终端输入。
从这里也可以看出Ctrl+D和换行符分别作为行结束符时,输出的不同结果。

EOF的作用也可以总结当终端有字符输入时,Ctrl+D产生的EOF相当于结束本行的输入,将引起getchar()新一轮的输入;当终端没有字符输入或者可以说当getchar()读取新的一次输入时,输入Ctrl+D,此时产生的EOF相当于文件结束符,程序将结束getchar()的执行。


~cout输出字符串和字符串地址


在C++中,如果我们想cout一个字符数组的话,那么它就会沿着这个地址,一直输出输出这个字符串,直到遇到'\0'。
char *c="cadn\0hello";
cout<<c<<endl;
输出的结果是:cadn

如果我们回归到C语言的话,例如用printf的话,如下:
printf("%x\n",&c[0]);
输出的结果是:46f020

C++标准库中的I/O类对<<操作符重载,因此在遇到字符型指针的时候会将其作为字符串名进行处理,输出指针所指的字符串
既然如此,我们就别让它知道那是字符串指针,所以,需要用到强制类型转换,把字符串指针转换成 无类型(或其他类型) 的指针。如下
cout<<(void *)c<<endl;
除了cout<<(char *)c<<endl都行



——c++输入字符串的多种方法

~cin >>

它不跳过特殊字符(空格、换行),cin在遇到空格符、回车符时会认为字符串已经结束,自动在New的结尾添加  \0字符


通过cin输入 Hello World 按下Enter键,则cin只将Hello提取给输入变量,此时World还继续留在输入队列中。

#include <iostream> 
using namespace std; 
main () 
{ 
int a,b; 
cin>>a>>b; 
cout<<a+b<<endl; 
}
输入:2[回车]3[回车] 
输出:5
注意:  >> 是会过滤掉不可见字符(如 空格 回车,TAB 等) 
cin>> noskipws >>input[j]           //不想略过空白字符,那就使用 noskipws 流控制


~cin.getline()和cin.get()

此二者与cin>>的区别:这两种输入方式只会在遇到回车符时认为字符串结束,在遇到空格符时还会继续读取空格后面的字符
cin.getline():在遇到回车符时,结束字符串输入并丢弃回车符
cin.get():在遇到回车符时,则会保留回车符在输入队列

当输入的字符长度大于数组长度时:
cin.getline():会设置失效位,后面的输入都无法再读取,可以利用这点来避免输入超过数组边界。但是cin.get():会继续将剩下的字符串保持在输入流中,但是会被吞掉一个字符(吞掉的字符就是被设置为空位符位置上的字符)。

1) cin.getline()

该函数有两个参数。第一个参数是用来储存输入行的数组名称,第二个参数是要读取的字符数。如果这参数为20,则函数最多读取19个字符,余下的空间用于储存自动在结尾处添加的空字符。
getline()成员函数在读取指定数目的字符或者遇到换行符时停止读取


上图说明:遇回车结束。


上图说明:‘\n’会被加到变量最后一位。由于getline指示读取5个字符大小的字符串,所以只读取了1234,最后一位由’\n’补齐。

getline()函数每次读取一行。它通过换行符来确定尾部,但不保存换行符。相反,在储存字符串时,它用空字符(’\0’)来替换换行符。

2) cin.get()

get(带参)不读取且不丢弃换行符,而是将其留在输入队列中。
例如:    cin.get(name, 10); cin.get(blog, 10); 
连续两个cin.get(带参).就出问题了。
由于第一次调用后,换行符将留在输入队列中,因此第二次调用时看到的第一个字符就是换行符。因此get()认为已经达到行尾,而没有发现任何读取内容。
如果不借助帮助,get()将不能跨过该换行符。

不带任何参数的 cin.get() ,可以读取下一个字符(即使是换行符)。
所以我们可以改成如下:
cin.get(name,10);
cin.get();
cin.get(blog,10);

cin.get(ch)和ch = cin.get()的区别:
ch = cin.get() :    先调用cin.get()函数,然后将该函数的返回值赋给ch,语句的结果为ch的值。
cin.get(ch):         在到达EOF值时,不会赋值给ch,ch只是接收输入字符。
属性     cin.get(ch) ch = cin.get()
传递输入字符的方式 赋值给参数ch 将函数返回值赋给ch
用于字符输入时函数的返回值    istream对象(执行bool转换后为true   ) int类型的字符编码
到达EOF时函数的返回值 istream对象(执行bool转换后为false   ) EOF
一般使用cin.get(ch),因其更符合对象方式,返回值是istream对象,这意味着可以讲它们拼接起来:cin.get(ch1).get(ch2);

注意:
char ch;
while( (ch = cin.get())!= EOF ) {}    ---正确(因为cin.get()那几个重载函数里面,只有不带参数的返回值是int型,而EOF为 -1)
while( cin.get(ch)!= EOF ) {}       ---错误(因为带参数的cin.get() 的返回值是流对象,即iostream对象)









3) cin.get(字符变量名)

可以用来接收一个字符


4) cin.get(字符数组名,接收字符数目)

用来接收一行字符串,可以接收空格。


上图说明:空格也能被接收。  刚好接收19个字符(20-1),最后一位由’\n’补齐。


~用string接收

1) getline()

接受一个字符串,可以接收空格并输出,需包含 #include<string>


说明:回车键结束输入。
和cin.getline()类似,但是cin.getline()属于istream流,而getline()属于string流,是不一样的两个函数

2) gets()

接受一个字符串,可以接收空格并输出,需包含 #include<string>