在C++中,有一个stream类,所有的I/O都以这个“流”类为基础的,包括文件的I/O,stream这个类有两个重要的运算符:
1、插入器(<<)
向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<”Write Stdout”<<’\n’;就表示把字符串”Write Stdout”和换行字符(‘\n’)输出到标准输出流。
2、析取器(>>)
从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据。
(所谓的流缓冲就是内存空间)
字符串支持整体输入输出(这就是要在末尾加上\0的原因)
判断一个数是不是素数:
设数m有两个因子i和j(且i小于或等于j),则i的平方一定小于或等于m,j的平方一定大于或等于m,所以根号m的值一定在i和j之间,所以如果m有因子,则一个因子一定小于或等于根号m。
bool isPrime(int n)
{if(n!=2&&n%2==0)
return 0;
for(int i=3;i*i<=n;i+=2)
if(n%i==0)
return 0;
return 1;
}
常用的流状态:
showpos 在正数(包括0)之前显示+号
showbase 十六进制整数前加0X,八进制整数之前加0
uppercase 十六进制格式字母用大写字母表示(默认为小写字母)
showpoint 浮点输出即使小数点后都为0也加小数点
boolalpha 逻辑值1和0用true和false表示
left 左对齐(填充字符填在右边)
right 右对齐(填充字符填在左边)
dec 十进制显示整数
hex 十六进制显示整数
oct 八进制显示整数
fixed 常规小数格式输出
scientific 科学记数法格式输出
与cout捆绑调用:
cout.width(int); //设置显示宽度
cout.fill(char); //设置填充字符
cout.precision(int); //设置有效位数(普通显示方式)或精度(定点或科学记数法方式)
与流出符<<连用:
setw(int) //使用这三个时都要包含头文件iomanip
setfill(char)
setprecision(int)
文件流:
与标准输入/输出一样,流被看作是一种设备,一种概念装备。只要将流与某个实际设备捆绑,对流的操作便是对实际设备的操作了。在C++中,对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream。fstream有两个子类:分别是用于读取文件的ifstream类(input file stream)和用于写入文件的ofstream类(output file stream)。ifstream是从硬盘到内存,ofstream是从内存到硬盘。
文件打开的格式就像定义一个对象:
ifstream fin(filename,openmode=ios::in);
ofstream fout(filename,openmode=ios::out);
ifstream和ofstream是类型名,表示输入和输出文件流,其定义的对象fin和fout就是文件流的名称。filename是外部文件名。openmode是打开方式,ifstream的默认打开方式是ios::in,表示输入方式,ofstream的默认打开方式是ios::out,表示输出方式。
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
int main()
{
ifstream fin("a.txt"); //需在同一个目录下创建一个名为a.txt的文本文件
ofstream fout("b.txt"); //程序运行时会自动创建b.txt
for(string str;getline(fin,str);)
{
cout<<str<<endl;
fout<<str<<endl;//由于是整行读入,文件中的每个换行符都被丢掉了
}
return 0;
}
switch case语句的使用:
使用switch语句直接处理多个分支(当然包括两个分支).其一般形式为:
switch(表达式)
{
case 常量表达式1:语句1;
break;
case 常量表达式2:语句2;
break;
……
case 常量表达式n:语句n;
break;
default:语句n+1;
break;
}
case常量表达式只是起语句标号作用,并不是该处进行条件判断。在执行switch语句时,根据switch的表达式,找到与之匹配的case语句,就从此case子句执行下去,不再进行判断,直到碰到break或函数结束为止;若没有找到与表达式匹配的case语句,则执行default语句。
switch(3)
{
case 1: cout<<1<<" ";
case 2: cout<<2<<" ";
case 3: cout<<3<<" ";
case 4: cout<<4<<" ";
case 5: cout<<5<<" ";
default: cout<<0<<" ";
}
输出: 3 4 5 0
switch(3)
{
case 1: cout<<1<<" ";
case 2: cout<<2<<" ";
case 3: cout<<3<<" ";
case 4: cout<<4<<" ";
default: cout<<0<<" ";
case 5: cout<<5<<" ";
}
输出: 3 4 0 5 (程序是否执行下去与default无关)
把string转换为int(在int的范围内):
string s;cin>>s;
int n=atoi(s.c_str());
substr方法:
string str="0123456789";
cout<<str.substr(3,5)<<endl; //输出 34567
功能:从一个字符串复制一个从指定位置(这个例子中为3)开始,并具有指定长度(这个例子中为5)的子字符串。
C/C++数据范围:
char -2^7到2^7-1 (1 Byte)
short -2^15到2^15-1 (2 Bytes)
unsigned short 0到2^16-1 (2 Bytes)
int -2^31到2^31-1 (4 Bytes)
unsigned int 0到2^32-1 (4 Bytes)
long == int -2^31到2^31-1 (4 Bytes)
long long -2^63到2^63-1 (8 Bytes)
double -2^1024到2^1024 (8 Bytes)
VC++6.0调试技巧:
逐过程调试—F10
逐语句调试—F11
跳到光标处—Ctrl+F10
跳出本循环—Shift+F11
设定断点—F9
删除所有断点—Ctrl+Shift+F9
开始编译 F7
重新编译 –Ctrl+F7
开始调试--F5
停止调试—Shift+F5
重新启动—Ctrl+Shift+F5
getline(cin,str); //输入一整行
for(int n;cin>>n;)
while(n--) (当循环体和循环变量无关,n也无需复用时使用)
cout<<string(10,'*')<<endl; 则输出"**********"
for(int n;cin>>n;)
{
double sum=0;
for(int i=0,a;i<n&&cin>>a;++i)
sum+=a;
cout<<sum/n<<endl;
}
char a;
for(int n;cin>>n>>a;) 正确
for(int n,char a;cin>>n>>a;) 错误for(int n,m=0;cin>>n;)
{
cout<<(m++?"\n":""); 两次结果之间空一行,且最后一次后无空行。
}
int n=pow(2.0,31)-1,m=n+1,s=n+10;
cout<<n<<endl;
cout<<n+1<<" "<<m<<endl; (两个值相同)
cout<<n+10<<" "<<s<<endl; (两个值相同)
cout<<n+1.0<<" "<<m<<endl; (两个值不相同)
cout<<n+10.0<<" "<<s<<endl; (两个值不相同)
位操作运算符是用来进行二进制位运算的,它又分为两类:移位运算符和逻辑位运算符。
移位运算符
移位运算符可以把整型变量中的内容向左或向右移动指定的位数。>>运算符把位向右移动,<<运算符把位向左移动,移出变量两端的位被舍弃。
所有的按位操作都可以处理任何类型的整数。
初始化一个变量number:
unsigned short number=16387U; (不带符号的字面量应在数字的最后添加字母U或u)
number=number<<2; //对这个变量的内容进行移位,并存储结果
移位运算符的左操作数是要移位的值,右操作数指定要移动的位数
移位运算:
把数值16387(100000000000011)向左移动两位,得到数值12(000000000001100),数值的这种剧烈变化是舍弃高位数字的结果。
把数值16387(100000000000011)向右移动两位,得到数值4096(001000000000000),向右移动两位相当于使该数值除以4。
只要没有舍弃位,向左移动n位就相当于把该数值乘以2的n次方。同样,向右移动n位就相当于把该数值除以2的n次方。
但要注意,数值移位时,如果舍弃了重要的位,结果就不是我们希望的那样了。
可是,这与乘法运算并没有不同。如果把2字节的数值乘以4,就会得到相同的结果,所以向左移位和相乘仍是等价的。出现问题的原因是相乘的结果超出了2字节整数的取值范围。
如果需要修改原来的值,可以使用 op= 赋值运算符。在这种情况下,可以使用>>=或<<=运算符。例如:
number>>=2和number=number>>2等价
这些移位运算符跟前面用于输入输出的插入和提取运算符有可能搞混。
例如,如果输出变量number向左移动两位的结果,就应编写如下的代码:
cout<<(number<<2);
其中,括号是必不可少的。没有括号,编译器就会把移位运算符解释为流插入运算符,从而得不到想要的结果。
可以把移位运算符应用于带符号和不带符号的整型数。但是,向右移位运算符对带符号整数类型的操作随系统的不同而不同,这取决于编译器的实现。在一些情况下,向右移位运算符会在左边空出来的位上填充0。在其他情况下,符号位向右移动,在左边空出来的位上填充1。
移动符号位的原因是为了保持向右移位和除法运算的一致性。
可以用char类型的变量来说明这一点,解释其工作原理。假定把value定义为char类型,其初始值为-104(十进制):
signed char value=-104;
其二进制表示为10011000。使用下面的操作把它向右移动两位:
value>>=2;
二进制结果为11100110。右边溢出了两个0,因为符号位是1,就在左边空出来的位上填充1。该结果的十进制表示是-26,这正好是value的值除以4的结果。当然,对于不带符号的整数类型的操作,符号位不移动,在左边空出来的位上填充0。
前面说过,在向右移位负整数时,其操作是已定义好的,所以实现该操作时不一定要依赖它。因为在大多数情况下,使用这些运算符是为了进行按位操作,此时维护位模式的完整性是非常重要的。所以,应总是使用不带符号的整数,以确保避免高阶位的移位。
逻辑位运算符:
~ (按位求反运算符) 它是一个一元运算符,可以反转操作数中的位,即1变成0,0变成1。
& (按位与运算符) 它对操作数中相应的位进行与运算。如果相应的位都是1,结果位就是1,否则就是0。
| (按位或运算符) 它对操作数中相应的位进行或运算。如果两个对应的位中有一个是1,结果位就是1。如果两个位都是0,结果就是0。
^ (按位异或运算符) 它对操作数中相应的位进行异或运算。如果相应的位的值不同,结果位就是1;如果相应的位相同,结果位就是0。
整数型内码是32位二进制补码数。一个整数型内码的每一位都只有0和1两种状态。
00000000000000000000000000001100 12
11111111111111111111111111110100 -12
因此可以通过位的与(&),或(|)操作来查看整数内码中的任何一位。
例如,要打印整数5的最高位,可以:
cout<<((5&1<<31)?1:0);
cout<<(5>>31&1); //将最高位移到最低位来打印,结果直接为1或者0
例如,要打印n的内码:
for(int i=31;i>=0;--i)
cout<<(n>>i&1);
或
for(int i=0;i<31;++i)
cout<<(n<<i<0);
C++中algorithm(算法)头文件是STL(Standard Template Library,即标准模板库)的算法部分,里边定义了各种算法,比如sort(排序)之类的。加上algorithm就可以使用STL库里的各种算法了。
#include<algorithm>
string str="abcdefg";
reverse(str.begin(),str.end()); //reverse()可以将内容倒过来摆放
cout<<str<<endl;
输出:gfedcba
for(int n;cin>>n;)
ivec.push_back(n);
reverse(ivec.begin(),ivec.end());//reverse()也可以用于vector
#include<algorithm>里面提供了两种专门用于排列的函数:
next_permutation(arr,arr+N);//升序
prev_permutation(arr,arr+N);//降序
(permutation 序列,排列;prev(=previous) 上一个;先前的)
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 4;
int arr[N] = {1,2,3,4};
int main()
{
while(next_permutation(arr,arr+N));//prev_permutation(arr,arr+N) 的话arr里的数据按降序排列
{
for(int i=0; i!=N; ++i)
cout<<arr[i];
cout<<endl;
}
return 0;
}
运行结果:
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1
Press any key to continue
next_permutation(start,end);//升序
prev_permutation(start,end);//降序
注意:函数要求输入的是一个序列的头指针和尾指针的下一个指针
如果输入的是一个数组:double a[5]
则:double* start = &a[0],double* end = &a[5];
(实际上数组的最后一个元素应该是a[4],也就是说end实际指向的应该是数组最后一个元素指针的下一个指针(end=start+N)
函数对输入的数组进行移动排列一次后,在函数退出前判断移动后的数组是否升序(或降序),如果不是升序(或降序)则函数返回bool变量false,否则返回true。函数对数组的移动排列采用递归方式。
sort函数的用法:
STL里面有个sort函数,可以直接对数组排序,复杂度为n*log2(n)。sort()定义在在头文件<algorithm>中,是标准模板库里的函数。已知开始和结束的地址即可进行排序,可以用于比较任何容器(必须满足随机迭代器),任何元素,任何条件,执行速度一般比qsort要快。
具体事例:
char ch[20]="sdasdacsdasdas";
cout<<ch<<endl;sort(ch,ch+14);
cout<<ch<<endl;
注意:缺省第三个参数是升序排序。
sort中一个改变排序顺序的例子如下(降序):
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int data[5];
for(int i=0;i<5;i++)
cin>>data[i];
sort(data,data+5,cmp);
return 0;
}
sort函数可以传两个或三个参数。第一个参数是要排序的区间首地址,第二个参数是区间尾地址的下一地址。也就是说,排序的区间是[a,b-1]。简单来说,有一个数组int a[100],要对从a[0]到a[99]的元素进行排序,只要写sort(a,a+100)就行了,默认的排序方式是升序。如需要对数组t的第0到len-1的元素排序,就写sort(t,t+len);对向量v排序也差不多,sort(v.begin(),v.end());排序的数据类型不局限于整数,只要是定义了小于运算的类型都可以,比如字符串类string。如果是没有定义小于运算的数据类型,或者想改变排序的顺序,就要用到第三个参数——比较函数。比较函数是一个自己定义的函数,返回值是bool型,它规定了什么样的关系才是“小于”。想把刚才的整数数组按降序排列,可以先定义一个比较函数cmp:
bool cmp(int a,int b)
{
return a>b;
}
排序的时候就写sort(a,a+100,cmp);
假设自己定义了一个结构体node:
struct node
{
int a;
int b;
double c;
};
有一个node类型的数组node arr[100],想对它进行排序:先按a值升序排列,如果a值相同,再按b值降序排列,如果b还相同,就按c降序排列。
就可以写这样一个比较函数:
bool cmp(node x,node y)
{
if(x.a!=y.a) return x.a
if(x.b!=y.b) return x.b>y.b;
return x.c>y.c;
}
排序时写sort(arr,arr+100,cmp);
=再看一个完整的实例,一道题目“文件名排序 ”:
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
struct node //定义一个结构体来表示文件,a代表文件名,b代表文件类型(要么"File"要么"Dir")
{
string a,b;
};
bool lt(string x,string y)
{
int i;
for(i=0;i<x.length();i++)
if(x[i]>='A'&&x[i]<='Z')
x[i]='a'+(x[i]-'A');
for(i=0;i<y.length();i++)
if(y[i]>='A'&&y[i]<='Z')
y[i]='a'+(y[i]-'A');
return x<y;
}
bool comp(node x,node y)
{
if(x.b!=y.b) return x.b<y.b; //自定义的比较函数,先按b值升序排列(也就是"Dir"排在"File"前面)
return lt(x.a,y.a); //如果b值相同,再按a升序排列,用的是刚才定义的lt函数
}
int main()
{
node arr[10001];
int size=0;
while(cin>>arr[size].a>>arr[size].b)
size++;
sort(arr,arr+size,comp);
for(int i=0;i<size;i++)
cout<<arr[i].a<<" "<<arr[i].b<<endl;
return 0;
}
sort函数的第三个参数可以用这样的语句告诉程序你所采用的排序原则:
less<数据类型>()//从小到大排序 sort(a,a+10,less<int>());
greater<数据类型>()//从大到小排序 sort(a,a+10,greater<int>());
#include<ctime>
double t=clock();
程序;
cout<<(clock()-t)/1000<<endl; //默认单位为毫秒
clock()函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock tick)数。
每个合数都可以写成几个质数相乘的形式。
C++ 二分法查找:
当数据量很大且数据是排好序的(升序或是降序)适宜采用该方法。
#include<iostream>
#define N 10
using namespace std;
int main()
{
int a[N]={5,13,16,18,23,28,35,49,51,60},x;
int front=0,end=N-1,mid=(front+end)/2;
cin>>x;
while(front<end&&a[mid]!=x)
{
if(a[mid]<x) front=mid+1;
if(a[mid]>x) end=mid-1;
mid=(front+end)/2;
}
if(a[mid]!=x)
cout<<"None"<<endl;
else
cout<<mid+1<<endl;
return 0;
}
#include<cstdlib>
int n=random(num); //random(num)函数返回一个0到num-1之间的随机数
int n=random(min,max); //random(min,max)函数返回一个介于min和max之间的随机数(包含min,但不包含max)
typedef (type definition 的缩写)(definition n.定义; 规定):
在计算机编程语言中用来为复杂的声明定义简单的别名。它本身是一种存储类的关键字,与auto、extern、mutable、static、register等关键字不能出现在同一个表达式中。
typedef声明,简称typedef,为现有类型创建一个新的名字,或称为类型别名,在结构体定义,还有一些数组等地方都大量的用到。
typedef int size;
此声明定义了一个int的同义字,名字为size。注意typedef并不创建新的类型。它仅仅为现有类型添加一个同义字。你可以在任何需要int的上下文中使用size:
void measure(size * psz);
size array[4];
size len = file.getlength();
std::vector<size> vs;
定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:
typedef char Line[81];
此时Line类型即代表了具有81个元素的字符数组,使用方法如下:
Line text, secondline;
getline(text);
在编程中使用typedef目的一般有两个:
一、给变量一个易记且意义明确的新名字
二、简化一些比较复杂的类型声明
memset
函数原型:void* memset(void* s,int ch,unsigned int n)
函数解释:将s中前n个字节用 ch 替换并返回 s。
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。
例如:把一个char a[20]中的每一个元素清零,是 memset(a,0,strlen(a))
一、清零结构类型的变量:
struct example
{
char csName[16];
int iSeq;
int iType;
};
对于变量
struct example stTest;
一般情况下,清空stTest的方法:
stTest.csName[0]='/0';
stTest.iSeq=0;
stTest.iType=0;
用memset就非常方便:
memset(&stTest,0,sizeof(struct example));
二、清零数组:
struct example TEST[10];
memset(TEST,0,sizeof(struct example)*10);
istringstream:
istringstream对象可以绑定一行字符串,然后以空格为分隔符把该行分隔开来。
#include<iostream>
#include<sstream>
using namespace std;
int main()
{
for(string str,line;getline(cin,line);)
{
istringstream sin(line); //名字可以自己定义(例如 sin)
while(sin>>str)
cout<<str<<endl;
}
return 0;
}
测试:
input:
abc df e efgeg ffg
ouput:
abc
df
e
efgeg
ffg
整数型内码是32位二进制补码数。一个整数型内码的每一位都只有0和1两种状态。
00000000000000000000000000001100 12
11111111111111111111111111110100 -12
因此可以通过位的与(&),或(|)操作来查看整数内码中的任何一位。
例如,要打印整数5的最高位,可以:
cout<<((5&1<<31)?1:0);
cout<<(5>>31&1); //将最高位移到最低位来打印,结果直接为1或者0
例如,要打印n的内码:
for(int i=31;i>=0;--i)
cout<<(n>>i&1);
或
for(int i=0;i<31;++i)
cout<<(n<<i<0);