C语言基本知识
前言
自己学过C语言,温习所写,皆为自身的心得体会,写的非常简短,不适合初学者看,写的一般,请多包涵。
C语言快速入门
#include <iostream>
#include <cstdio>
变量:用来存放数据
%d:表示等待输入整数
&a:表示把输入的整数放入变量a
程序的注释:多行注释、单行注释
变量和数据类型
变量代表系统分配的内存空间。
名字决定地址,类型决定字节。
先定义,再使用或者说引用。
变量名不能和C++预留的保留字相同。
数据类型说明一个变量表示什么样的数据。
数据类型的自动转换:不同数据类型的数据可以互相赋值。
整数转化为字符型只保留最右边的一个字节。
整型和浮点数转化。
常量
值不会改变的量。
十六进制以“0x”开头。
八进制以“0”开头。
0-9:48-57
A-Z:65-90
a-z:97-122
\n:10换行,输出位置下一行
\r:13回车,输出位置本行开头
\t:9制表符,输出位置下一个制表符开头
字符型常量单引号char,字符串常量双引号
字符串常量中转义字符也可以生效(\,",’)。
符号常量:宏定义#define 名字 值,比数值常量方便。
运算符
格式控制符%d%f%c等等。
scanf("%d%c",&n,&c);//此时输入34k,中间不要加空格
输入字符不会跳过空格,其他类型会跳过。
scanf有除了%之外的非控制字符,输入时也有,则会跳过。
scanf("%d %c",&n,&c);//此时输入的时候可以添加空格
控制printf输出的宽度:%nd空格来补,%0nd零来补。
控制printf输出的浮点数的精度:%.nf。
cin进行输入,cout进行输出。
cin.get作用:从标准输入吸收任何字符(包括回车和空格),优先吸收cin缓存(之前cin的残留内容)。
#include <iostream>
using namespace std;
int main()
{
char ch;
cout << "This program has paused. Press Enter to continue.";
cin.get(ch);
cout<<(int)ch<<endl;
cout << "It has paused a second time. Please press Enter again."; ch = cin.get();cout<<(int)ch<<endl;
cout << "It has paused a third time. Please press Enter again.";cin.get();cout<<(int)ch<<endl;
cout << "Thank you! \n";
return 0;
}
***cin.get()一次只能读取最早缓存的一个字符,可以一次性输入多个字符,但是读取的时候只能一个一个的读取。
cin.get()读取输入的所有字符,会返回一个大于0的整型,读不到会返回-1
//读入所有输入的字符:即显示输入的字符串
#include <iostream>
using namespace std;
int main()
{
int c;
while((c=cin.get())!=EOF)
{
cout<<(char)c;
}
return 0;
}
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int c;
while(scanf("%c",&c)!=EOF)
{
printf("%c",c);
}
return 0;
}
只有^z才可以结束输入。
赋值运算符
算术运算符
++a返回+1后的值
a++返回+1前的值
关系运算符
逻辑运算符:&& || !
逻辑表达式的短路计算
强制类型转换运算符
优先级
++,–,!最高了
算术
关系
逻辑
赋值
条件分支结构和循环结构
if语句和switch语句
for语句和while语句
break语句
出现在循环体中,用来跳出当前的一层循环。
continue语句
出现在循环体中,用来结束本次循环,不跳出当前循环。
freopen重定向输入
输入由键盘重定向为文件,运行时就可以不用输入数据了。
例:freopen(“C:\test.txt”,“r”,stdin);//包括转义字符
数组
一维数组定义。
元素一个挨一个的连续存放。
筛法求n以内的所有素数:去掉46810,去掉69,最后生下了2357。
(标志数组isPrime,1是素数,0不是素数)
数组的初始化。
数组越界
下标的值为负数或者太大了。
二维数组
函数
一些实现了某种功能的需要反复使用的代码包装而成的功能模块。
函数调用语句进入函数执行,返回值语句进入主函数执行。
函数声明在函数的调用之前,函数定义不一定要在函数调用之前。
函数声明又叫做函数原型,参数名称可以忽略,但是参数的类型必须要有。
形参是实参的拷贝,形参改变不影响实参,除非形参是数组。
一维数组作为函数的参数,不用写出数组的元素个数,形参数组改变,实参数组也改变。
二维数组作为函数的参数,不用写出有多少行,但是必须要写出有多少列。
递归:一个函数自己调用自己。
递归必须要有终止条件,否则会无限制的循环下去的。
库函数:编译器自带的函数。
头文件:包含许多库函数的声明以及其他信息。
位运算:与&、或|、异或^、非~、左移<<、右移>>。
与:某些位清零其他位不变,获取某变量中的某一位。
或:某些位置一其他位不变。
异或:某些位取反其他位不变。
左移:高位丢弃,低位补零。左移n位,相当于乘以2的n次方。
右移:符号位为1,补1;高位为0,补0。右移n位,相当于除以2的n次方。
字符串
字符串常量:字符数目+1,还有一个结尾字符‘\0’。
双引号要写为",\要写为\。
用cin读入字符串:回车为止
cin<<一维字符数组名
cout>>一维字符数组名
用scanf读入字符串:空格/回车为止
scanf("%s",一维字符数组名);
printf("%s",一维字符数组名);
***二者都会自动添加结尾字符,可能会发生数组越界。
用gets读入字符串:回车为止
gets(一维字符数组名)
字符串库函数
cstring,字符串函数都以’\0‘来判断字符串的结尾,实参可以是一维字符数组或者字符串常量。
字符串拷贝:strcpy后面拷贝到前面。
字符串比较大小:int strcmp,返回0则相等。
求字符串的长度:int strlen。
字符串拼接:strcat,后面的加到前面的后面。
字符串转化为大写:strupr。
字符串转化为小写:strlwr。
指针
一个变量,代表了一个内存地址。
指针变量的类型是int*、char*等等。
*p是一个表达式,代表着从地址p开始的sizeof(T)个字节的内容。
*是间接引用运算符。
指针指向变量==变量地址赋值给指针。
&取地址运算符。
意义:指针可以让人们直接自由访问内存空间,不仅限于变量所在的存储空间。
***在写一些接近硬件的程序,比如硬件驱动程序、病毒等等,就需要不仅限于变量所在的内存空间。
指针的互相赋值,必须是相同类型,如果不是相同类型必须先进行强制类型转化。
两个同类型的指针可以比较大小。
两个同类型的指针可以相减。
指针变量加减一个整数的结果仍然是指针。
指针变量可以自增、自减。
指针可以用下标运算符[ ]进行运算。
空指针:指向地址0的指针。
地址0不能访问。
指针可以作为条件表达式来使用,if(p)。
指针作为函数参数。(没有违背形参是实参的拷贝,此时形参确实是实参的拷贝,只不过实参是地址,此时形参拷贝的是地址,使用的时候使用的是地址指向的内容,而不是直接使用的形参,因此操作额结果并不会发生改变)
数组的名字就是一个指针常量。
二维数组T a[N][N]中,a[N]是一个类型为T *的指针。
指向指针的指针。
字符数组名的类型是char星。
字符串操作库函数
char *strchr:找到字符c第一次出现的位置。
char *strstr:寻找子串第一次出现的位置。
int stricmp:大小写无关的字符串比较。
int strncmp:前n个子串的大小比较。
char *strncpy:只拷贝前n个字符。
char *strtok:从字符串中逐个抽出被某些字符分隔的子串。
void指针
内存操作库函数
void *memset:将从字符串某位置开始的n个字节都设置成某个初始值。
void *memcpy:将从地址开始的n个字节拷贝到另一个字节中去。
函数指针:指向函数的指针变量,int(*p)(int char);。
***函数指针的调用:函数指针名赋值,函数指针名(参数)。
qsort快速排序库函数:起始地址,元素个数,元素大小,排序规则。
(排序规则函数中,如果返回值小于0,那么排在前面)。
结构
一种自定义的数据类型,会有一个名字,这个名字可以用来定义变量。
访问结构变量的成员变量。
结构数组。
指向结构变量的指针(也可以通过指针访问成员变量)。
各种变量
全局变量
局部变量
静态变量:全局+static的局部,地址一定不变,非静态每次都可能会发生改变。
变量的作用域:作用域大的被小的屏蔽。
变量的生存期:在此期间,变量占有内存空间,且只能归它使用。
选择排序(n*n)
从前往后依次找最小的那个。
插入排序(n*n)
有序的在左边,无序的在右边,开始有序的就只有a[0],每次取出无序数组的最左边的一个元素加入到有序数组中去,直到无序中没有元素。
冒泡排序(n*n)
有序的在右边,无序的在左边,开始有序的啥也没有,无序的从左到右找最大的放到有序的最左边,依次这么进行下去,直到无序的没有任何元素为止。
快速排序(n*logn)
二分查找(logn)
STL初步
STL:标准模板库,其中包括一些常用的算法和数据结构。
使用算法#include “algorithm”
1.用sort进行排序
sort(数组名+n1,数组名+n2)。
将数组中下标范围为[n1,n2)的元素从小到大排序。
sort(数组名+n1,数组名+n2,greater())。
将数组中下标范围为[n1,n2)的元素从大到小排序。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
struct Rule1{
bool operator()(const int &a1,const int &a2)const{
return a1>a2;//按从大到小排序
}
};
struct Rule2{
bool operator()(const int &a1,const int &a2)const{
return a1%10 < a2%10;//按个位数从小到大排序
}
};
void Print(int a[],int size)
{
for(int i=0;i<size;++i)
cout<<a[i]<<",";
cout<<endl;
}
int main()
{
int a[]={12,45,3,98,21,7};
sort(a,a+sizeof(a)/sizeof(int));
Print(a,sizeof(a)/sizeof(int));
sort(a,a+sizeof(a)/sizeof(int),Rule1());
Print(a,sizeof(a)/sizeof(int));
sort(a,a+sizeof(a)/sizeof(int),Rule2());
Print(a,sizeof(a)/sizeof(int));
return 0;
}
sort(数组名+n1,数组名+n2,排序规则结构名())。
排序规则结构定义:
struct 结构名
{
bool operator()(const T &a1,const T &a2)const{
//a1应该排在前面,返回true,否则返回false
}
}
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
struct Student{
char name[20];
int id;
double gpa;
};
Student students[]={
{"Jack",112,3.4},{"Mary",102,3.8},{"Mary",117,3.9},
{"Ala",333,3.5},{"Zero",101,4.0}
};
struct StudentRule1{//按姓名从小到大排序
bool operator()(const Student &s1,const Student &s2)const{
if(stricmp(s1.name,s2.name)<0)
return true;
return false;
}
};
struct StudentRule2{//按id从小到大排序
bool operator()(const Student &s1,const Student &s2)const{
return s1.id<s2.id;
}
};
struct StudentRule3{//按gpa从高到低排序
bool operator()(const Student &s1,const Student &s2)const{
return s1.gpa>s2.gpa;
}
};
void PrintStudents(Student s[],int size)
{
for(int i=0;i<size;++i)
cout<<"("<<s[i].name<<","
<<s[i].id<<","<<s[i].gpa<<")";
cout<<endl;
}
int main()
{
int n=sizeof(students)/sizeof(Student);
sort(students,students+n,StudentRule1());
PrintStudents(students,n);
sort(students,students+n,StudentRule2());
PrintStudents(students,n);
sort(students,students+n,StudentRule3());
PrintStudents(students,n);
return 0;
}
2.二分查找算法
在从小到大的数组中进行二分查找。
binary_search(数组名+n1,数组名+n2,值)。
自定义排序规则、任何类型数组中进行二分查找。
binary_search(数组名+n1,数组名+n2,排序规则结构名())。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
struct Rule{
bool operator()(const int &a1,const int &a2)const{
return a1%10<a2%10;
}
};
void Print(int a[],int size){
for(int i=0;i<size;++i){
cout<<a[i]<<",";
}
cout<<endl;
}
int main()
{
int a[]={12,45,3,98,21,7};
sort(a,a+6);
Print(a,6);
cout<<binary_search(a,a+6,12)<<endl;
sort(a,a+6,Rule());
Print(a,6);
cout<<binary_search(a,a+6,12)<<endl;
cout<<binary_search(a,a+6,8,Rule())<<endl;
return 0;
}
用lower_bound()二分查找下界(大于等于)。
用upper_bound()二分查找上界(大于)。
3.multiset
希望增加删除数据、查找数据都能快速完成,可以用平衡二叉树数据结构存放数据,具体体现在multiset,set,multimap,map四种”排序容器“中。
multiset<type> st;
st中可以存放type类型的数据并且自动从小到大排序
st.insert
st.find
st.erase
复杂度都是log(n)
#include <iostream>
#include <cstring>
#include <set>//multiset和set的头文件
using namespace std;
int main()
{
multiset<int> st;
int a[10]={1,14,12,13,7,13,21,19,8,8};
for(int i=0;i<10;i++)
{
st.insert(a[i]);//插入的是a[i]的复制品
}
multiset<int>::iterator i;//迭代器,类似于指针,必须通过迭代器来访问容器中的元素
for(i=st.begin();i!=st.end();++i)
cout<<*i<<",";//*i类似于指针的*p,表示指针指向的内容
cout<<endl;
}
迭代器
multiset<T>::iterstor p;
迭代器类似于指针,不同的是,迭代器只能++和–,只能比较!=或者==,不能比较大小,不能加减整数,也不能相减。
st.begin()返回值为迭代器类型,指向头一个元素的迭代器
st.end()返回值为迭代器类型,指向最后一个元素的后一个迭代器
//查找
i=st.find(22);
if(i=st.end())cout<<"not found"<<endl;
st.insert(22);
//成员函数的使用
//寻找13之前的第一个元素的迭代器
i=st.low_bound(13);
cout<<*i<<endl;
//寻找8之后的第一个元素的迭代器
i=st.upper_bound(8);
cout<<*i<<endl;
//删除迭代器i指向的元素
st.erase(i);
for(i=st.begin();i!=st.end();++i)
{
cout<<*i<<" ";
}
return 0;
自定义排序规则的multiset
#include <iostream>
#include <cstring>
#include <set>
using namespace std;
struct Rule1{
bool operator()(const int &a,const int &b){
return (a%10)<(b%10);
}//返回值为true说明a的个位数必须小于b的个位数
};
int main()
{
multiset<int,greater<int>> st;//排序规则为从大到小
int a[10]={1,14,12,13,7,13,21,19,8,8};
for(int i=0;i<10;++i)
st.insert(a[i]);
multiset<int,greater<int>>::iterator i;
for(i=st.begin();i!=st.end();++i)
cout<<*i<<",";
cout<<endl;
multiset<int,Rule1> st2;//排序规则为个位数从小到大
int a[10]={1,14,12,13,7,13,21,19,8,8};
for(int i=0;i<10;++i)
st.insert(a[i]);
multiset<int,Rule1>::iterator p;
for(p=st.begin();p!=st.end();++p)
cout<<*p<<",";
cout<<endl;
p=st2.find(133);
cout<<*p<<endl;//输出13
//find(x):在容器中找一个元素y,使得”x必须排在y前面“和”y必须排在x前面“都不成立,那么我们就认为二者相等,并不是一定要x==y
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
struct Student{
char name[20];
int id;
int score;
};
Student students[]={{"Jack",112,78},{"Mary",102,85},{"Ala",333,92},{"Zero",101,70},{"Cindy",102,78}};
struct Rule{
bool operator()(const Student &s1,const Student &s2)const
{
if(s1.score!=s2.score)
return s1.score>s2.score;
else
return (strcmp(s1.name,s2.name)<0);
}
};
int main()
{
multiset<Student,Rule> st;
for(int i=0;i<5;++i)
st.insert(student[i]);
multiset<Student,Rule>::iterator p;
for(p=st.begin();p!=st.end();++p)
cout<<p->score<<" "<<p->name<<" "<<p->id<<endl;
Student s={"Mary",1000,85};
p=st.find(s);
if(p!=st.end())
{
cout<<p->score<<" "<<p->name<<" "<<p->id<<endl;
}
return 0;
}
4.set
和multiset的区别在于容器中不能有重复的元素(重复:并不是”==“)(插入元素可能不成功)。
#include <iostream>
#include <cstring>
#include <set>
using namespace std;
int main()
{
set<int> st;
int a[10]={1,2,3,8,7,7,5,6,8,12};
for(int i=0;i<10;++i)
st.insert(a[i]);
cout<<st.size()<<endl;//输出8,重复的元素不占
set<int>::iterator i;
for(i=st.begin();i!=st.end();++i)
cout<<*i<<",";//输出1 2 3 5 6 7 8 12,没有重复元素
cout<<endl;
}
//插入写法
pair<set<int>::iterator,bool>=st.insert(2);
if(!result.second)
cout<<*result.first<<"already exists."<<endl;
else
cout<<*result.first<<"inserted."<<endl;
return 0;
5.multimap
multimap<T1,T2> mp;//元素都是pair类型
struct{
T1 first;//关键字
T2 second;//值
};
mp中元素按照first排序,可以按照first进行查找。
#include <iostream>
#include <map>
#include <cstring>
using namespace std;
struct StudentInfo{
int id;
char name[20];
};
struct Student{
int score;
StudentInfo info;
};
typedef multimap<int,StudentInfo> MAP_STD;//给类型起个别名
int main()
{
MAO_STD mp;
Student st;
char cmd[20];
while(cin>>cmd)
{
if(cmd[0]=='A')//插入
{
cin>>st.info.name>>st.info.id>>st.score;
mp.insert(make_pair(st.score,st.info));
}
else if(cmd[0]=='Q')//查找
{
int score;
cin>>score;
MAP_STD::iterator p=mp.lower_bound(score);//迭代器,寻找比score小的第一个元素
if(p!=mp.begin())//说明找到了符合要求的元素
{
--p;
score=p->first;//比要查询的分数低的最高分
MAP_STD::iterator maxp=p;
int maxId=p->second.id;
for(;p!=mp.begin()&&p->first==score;--p)
{
if(p->second.id>maxId)
{
maxp=p;
maxId=p->second.id;
}
}
}
if(p->first==score)
{
if(p->second.id>maxId)
{
maxp=p;
maxId=p->second.id;
}
}
cout<<maxp->second.name<<" "
<<maxp->second.id<<" "
<<maxp->first<<endl;
}
else
cout<<"Nobody"<<endl;
}
return 0;
}
6.map
不能有重复的元素,可以使用[ ],下标为关键字,返回值为关键字相同的元素的second,有可能会插入失败。
#include <iostream>
#incldue <map>
#include <string>
using namespcae std;
struct Stduden{
string name;
int score;
};
Student students[5]={{"Jack",89},{"Tom",74},{"Cindy",87},{"Alysa",87},{"Micheal",98}};
typedef map<string,int> MP;
int main()
{
MP mp;
for(int i=0;i<5;++i)
{
mp.insert(make_pair(students[i].name,students[i].score));
}
cout<<mp["Jack"]<<endl;//输出为89
mp["Jack"]=60;//修改second为60
for(MP::iterstor i=mp.begin();i!=mp.end();++i)
cout<<"("<<i->first<<","<<i->second<<<")";
cout<<endl;
Stdudent st;
st.name="Jack";
st.score=99;
pair<MP::iterator,bool>p=mp.insert(make_pair(st.name,st.score));
if(p.second)
cout<<"("<<p.first->first<<","<<p.first->second<<")inserted"<<endl;
else
cout<<"insertion failed"<<endl;
mp["Harry"]=78;
MP::iterator q=mp.fond("Harry");
cout<<"("<<q->first<<","<<q->second<<")"<<endl;
return 0;
}
#include <iostream>
#include <set>
#include <map>
#include <string>
using namespace std;
struct Wore{
int times;
string wd;
};
struct Rule{
bool operator()(const Word &s1,const Word &s2)const
{
if(w1.times!=w2.times)
return w1.times>w2.times;
else
return w1.wd<w2.wd;
}
};
int main()
{
string s;
set<Word,Rule> st;
map<string,int> mp;
while(cin>>s)++mp[s];
for(map<string,int>::iterator i=mp.begin();i!=mp.end();++i)
{
Word tmp;
tmp.wd=i->first;
tmp.times=i->second;
st.insert(tmp);
}
for(set<Word,Rule>::iterator i=st.begin();i!=st.end();++i)
cout<<i->wd<<" "<<i->times<<endl;
}