ifstream fin(“a.out”);getline(fin,str);//从键盘读取fin>>n;fin>>ch;fin.close();fin.read(地址,字节数);将文件中的内容读取到内存当中。
3.istream常用函数:
A.get()读取一个字符,返回字符ASCII码;get(char&)读取单个字符,保存到一个istream对象的引用。例如char c;cin.get(c);
B.C风格:getline(字符数组名,长度):读取一行,遇到回车停止。C++风格:getline(in,str);此外可以指定结束符,默认为\n。getline(in,str,’\t’);
C.peek():查看而不读取输入流中下一个字符的ASCII码。例如:char c = cin.peek();
D.putback(ch):向输入流中插入一个字符串。
E.ignore(很大的数如1000,最后的字符);清空缓冲区。
F.fout.write(地址,字节数);将内存中的内容写到文件中
G.fin.read(地址,字节数);将文件中的内容读到内存当中
第十三天:
1.I/O包括控制台(cin/cout(有缓冲),cerr(无缓冲),clog(一般不用,类似cerr))和文件(自己创建,ifstream/ofstream)。控制台和文件输入输出流的用法类似。
2.输出控制标志:cout.setf(ios::left);清除控制标志:coutunsetf(ios::left);设置输出宽度cout.width(10);设置填充cout.fill(‘#’)、precision(3)设置精度;
3.输出控制符:flush清空缓冲区、endl添加\n并清空缓冲区、oct设置八进制、dec、hex。
4.内部类:在函数或者类中定义的类成为内部类。类中的内部类称之为成员内部类A::B objb,函数中内部类称之为局部内部类。
5.异常用法:try{if(exception){throw data}}catch(data type){...}抛出子类对象,捕获父类对象可以捕获到->子类对象一定是父类对象。因此,一般throw子类对象,catch父类对象。
第十四天:
1.线性表:数组、链表、栈(FILO,只能在同一端进行插入和删除)、队列(FIFO,必须在不同端进行插入和删除)。
2.非线性表:树、图。
3.二叉树不同于二叉查找树(BST)。二叉查找树要求:节点的左子树的值小于父节点,节点的右子树的值大于或等于父节点。二叉查找树查找数据快速,类似于这般查找。二叉查找树数据结构:
struct Node
{
T data; Node* left;Node* right;
Node(const T& t):data(t),left(NULL),right(NULL){}
}
4.二叉树的遍历:
递归遍历:
void travel(Node* tree)
{
if(tree == NULL){return;}
cout<data<<’ ’; ①
travel(tree->left); ②
travel(tree->right); ③
}
①->②->③先根遍历、②->①->③中根遍历、③->②->①后根遍历
最常用的是中根遍历,对于二叉查找树而言,中跟遍历自动排序。
5.清空二叉树:
void clear(Node*& tree)
{
if(tree == NULL){return;}
clear(tree->left);clear(tree->right);
delete tree;tree = NULL;//使用引用的原因是这一句使tree=Null,如果不使用引用,tree是临时变量,真正的tree并不等于Null。
}
6.统计节点数:
void size(void* tree)
{
if(tree == NULL) {return 0;}
return size(tree->left)+size(tree->right)+1;
}
7.二叉查找树插入结点:
void insert(Node*& tree,Node* p)
{
if(tree == NULL){tree=p;}
else if(p == NULL){return}
else if(p->datadata){insert(tree->left,p)}
else{insert(tree->right,p)}
}
8.二叉查找树查找数据:
Node*& find(Node* tree, const T& t)//&的原因是返回的结点可供修改
{
if(tree == NULL){return tree;}//不能返回NULL
if(tree->dafta == t){return tree;}//根就是tree
else if(tdata){return find(tree->left,t);}
else {return find(tree->right,t)};
}
9.二叉查找树删除结点:删除->指向并合并左右分支->释放删除节点
void erase(Node*& tree,const T& t)
{
Node*& p = find(tree,t);
if(p==Null){return;}
//合并左右分支
insert(p->right,p->left);
//保存并指向新分支
Node* q = p;
p = p->right;
//删除
delete q;
}
10.二叉查找树修改:
void update(Node*& root,const T& o,const T& n)
{
Node* p = find(root,o);
if(p == NULL){retrun;}
erase(root,o);
P=new Node(n);
insert(root,p);
}
11.算法时间复杂度O(n),大O表示:最多怎样怎样。线性查找复杂度:O(n)。二分查找(折半查找):O(logN)。
12.常用算法设计策略:
A.暴力法:穷举所有可能性。
B.递归法:最常用的算法设计思想,体现在许多优秀算法之中。
C.分治法:分而治之的算法思想,体现一分为二的哲学思想。
D.模拟法:用计算机模拟实际场景,经常用于与概率有关的问题。
E.贪心法:采用贪心策略的算法设计。
F.优化法:利用生物学优选原理。
第十五天:
1.排序算法:
A.选择排序:O(N2)
B.冒泡排序:O(N2)
C.插入排序:O(N2)
D.快速排序:O(N*logN)
2.自定义模板:
函数模板:
template
void disp(T* a, int n)
{
for(int i=0;i
}
系统调用时自动匹配类型。系统优先选择非模板函数。使用模板尽量声明和定义不分开。模板保存在头文件中。
类模板:
template
class Stack
{
T name;
};
使用类模板,必须指定模板类型。
第十六天:
1.STL包括类模板(容器)和函数模板(通用算法)。所有的容器中,都有一个内部类,称之为迭代器。函数模板(通用算法)通过迭代器处理数据。因此,迭代器是桥梁。
2.容器包括:序列式容器{vector(动态数组),deque(双边队列,支持在头尾插入、删除数据)、list(双向链表)}和关联式容器{map(映射)、multimap(多映射)、set(数据集)、multiset(多数据集)}。所有关联式容器都使用二叉查找树模板,自动排好顺序。
3.容器适配器:stack、queue、priority_deque(优先队列,不管放入顺序,最大的先出来)
4.通用算法(通过迭代器操作容器):
查找for_each/find/find_first_of/find_end、排序sort/reverse、复制copy/copy_backward、修改replace/merge/remove、数值m in/max/count/swap/accumulate
第十七天:
1.查看命令帮助:man -a mkdir 模糊查询:man -k dir
2.Unix使用ps命令查找进程id。查看环境变量env、查看当前目录pwd、删除整个目录rm -r、新建目录mkdir、设置环境变量export name=value(仅限当前系统进程)、显示环境变量值echo $name、显示当前用户名whoami
3.显示所有环境变量:
int main(int ac, char* av[], char* env[])
{
for(int i=0;env[i]!=NULL;i++){cout<
}
获取环境变量值:getenv(name);使用man getenv查看需包含头文件;
设置环境变量:char* var = “class=3”;putenv(var);仅在本进程及其子进程有效;
4.进程的状态:O进程正在进行、S进程等待cpu调度、R进程准备完毕但尚未执行、T进程被挂起、Z僵死进程
5.Include、getlogin获取用户登录名、getuid获取当前登录用户的用户id、geteuid获取当前用户的有效id、getpwuid得到指向passwd结构的指针,该结构中包括用户相关信息记录(passwd结构包括getpwuid获取的id,getpwnam获取的name等)。返回当前目录getcwd
6.目录操作:打开目录opendir、读取目录readdir、关上目录closedir、新建目录mkdir、删除目录rmdir、设置目录为当前chdir、重命名rename或者mv
第十八天:
1.获取主机名:gethostname、获取操作系统名:getuname
2.时间:time_t time(time_t *mem);mem一般设置为NULL,time表示从1970.1.1到现在的秒数。
当前时间:(+8考虑时区)
time_t t1(NULL);int s=t1%60;int m=(t1/60)%60;int h=(t1/3600+8)%24;
时间函数:
time_t t = time(NULL);tm* p =localtime(&t);cout<tm_year+1990<<”年”<tm_mon+1<<”月”<tm_mday<<”日 星期”<tm_wday<<” ”<tm_hour<<”:”<tm_min<<”:”<tm_sec<
strftime();使用更简洁,电子时钟:
for(;;)
{
time_t t = time(NULL); tm* p = localtime(&t); char buf[100];
strftime(buf,100,”%F 星期%W %T”,p);
cout<<’\r’<
//while(t==time(NULL));//1s更新一次,始终占用系统资源
sleep(1);//睡1s,大致时间,不准确
}
内核time->time_t t;->localtime(&t);->tm*p;->strftime(...)->char buf[];
3.默认ps只查看本终端进程;ps -u uid查看特定uid用户的进程
4.登记退出处理函数:atexit(函数名);程序结束时调用,与调用位置无关。注册函数调用按照栈规则。
5.全局变量(在main外面)的析构在程序结束后调用。exit(0):结束程序,之后的代码不再执行(C和C++有区别(C中没有析构),对于局部对象的析构,exit之后不再执行)。exit不析构局部对象。_exit直接退出,什么析构都不做。Abort、terminate也是非正常结束。
6.使用ps -l查看进程状态。system(命令);getpid获取进程id、getppid获取父进程id。
7.Unix标志性函数:fork将一个进程完全复制一份,每个进程有个字的内存空间等,每个进程有独立的进程id。一次调用,两次返回。父进程返回子进程id(出错返回负数),子进程返回零。包含unistd.h。用法:pid_t id = fork();fork后面的语句“执行两次”。
8.子进程的资源回收需要用父进程回收。如果父进程结束,子进程还没结束,该子进程称之为孤儿进程。Init进程(id为1)会成为所有孤儿进程的父进程,init进程称之为孤儿院。如果父进程还在,子进程结束,父进程照常工作,子进程处于Z(僵死)状态,占用系统资源,需要自己回收(wait函数)。
9.wait函数用于回收子进程资源:wait(NULL);wait等待子进程结束,然后回收子进程资源。
10.Fork使用时容易产生父进程、子进程代码混在一起的现象,因为fork产生的父进程、子进程用友完全的内存结构。exec可以完成父子进程的代码分离。exec系列函数在进程空间中装入新程序来覆盖旧程序。新程序从头开始执行。execvp(程序名,argv);argv第一个是程序名最后一个是NULL作为结束标志,传参数给新程序的main中的argv。execlp(程序名,程序名,参数...);
第十九天:
1.进程间通信(IPC)内容包括:信号机制、FIFO(文件队列/命名管道)和消息队列。
2.文件标示符:输入0,输出1,错误2。例如屏幕输出可写为:write(1,”ab\n”,3);
3.使用fork父进程结束后,子进程可能还在运行,这时可以设置子进程无法进行一些操作,例如不能输出:close(1);此时的子进程就是后台服务程序。该子进程称之为守护进程,也叫精灵进程daemon。精灵进程的规范写法:
A.fork后让父进程结束
B.子进程中使用setsid()函数,新建会话组(脱离父子进程关系)
C.设置新的权限屏蔽umask(0077);第一个零开头表示八进制。
D.关闭所有文件描述符close(0到255)。
E.循环工作。
4.makefile文件能够避免因为修改产生的全部编译、连接。提高编译效率。
A.文件名为makefile,使用#表示注释
B.里面写的是依赖关系(目的文件:依赖文件)和编译命令:
a.out : a.o b.o c.o (a.out依赖于a.o,b.o,c.o)
(TAB)g++ a.o b.o
a.o : d.cc f.h
(TAB)g++ -c d.cc
b.o : e.cc
(TAB)g++ -c e.cc
C.执行:make
D.$@表示所有目的文件,$^表示所有依赖文件
5.查看所有内容:man -a sleep 模糊查看帮助:man -k eep
6.信号,理解为软中断
A.SIGABRT:调用abort函数产生此信号
B.SIGALRM:超时信号
C.SIGCHLD:子进程终止时调用此信号
D.SIGINFO:CTRL+T状态键产生此信号
E.SIGINT:DEL OR CTRL+C
F.SIGIO:表示一个异步IO事件
G.SIGKILL:强制杀死进程信号
H.SIGTERM:terminate发出信号,kill进程时,进程会受到此信号
I.SIGUSR1:用户自定义信号
SIGKILL和SIGSTOP信号无法捕获。
7.信号处理步骤:登记(signal函数(信号,函数名),返回旧函数名(旧函数时信号默认的处理函数))->接受调用。某些系统处理信号,登记一次有效。可以在登记函数中再次登记。signal(信号,SIG_IGN);用于忽略某信号,signal(信号,SIG_DFL);缺省处理,返回SIG_ERR表示登记失败。
8.发信号方式:
A.kill(进程ID,信号);仅限于给自己的进程发信号
B.kill(getpid(),信号);或者raise(信号);给自己发信号
9.处理子进程的结束:
void fun(int sig)
{
signal(sig,fun);
wait(NULL);
}
int main()
{
signal(SIGCHLD,fun);
}
10.对于复杂的进程间消息传输,需要使用的是FIFO(管道pipe)和消息队列。
管道pipe:单相数据流动。mkfifo创建管道。返回负值表示创建失败。
进程A,注释为进程B。管道双方AB都运行才能工作,双方关闭后,数据全部消失。
int main()
{
mkfifo(“a.fifo”,0700);int fd=open(“a.fifo”,O_WRONLY);
//int fd = open(“a.fifo”,RD_ONLY);
if(fd<0){cout<<”error”<
cout<<”Pipe ready!”<
while(1)
{
cout<<”input text:”<
write(fd,str.c_str(),str.size());
if(str==”bye”){break};
/*
char buf[100]; int n; n = read(fd,buf,100);buf[n]=’\n’;
cout<
*/
}
close(fd);
}
11.消息队列:由内核维护的,在内存中传递数据。命令ipcs显示消息队列状态。命令ipcrm -q messagequeID删除消息队列。消息队列函数:(包含头文件:sys/ipc.h、sys/msg.h)。消息队列中通过通道传递数据,通道中的数据结构必须为满足:第一个为long型通道号(>0)。然后是任意多个、任意类型的数据。
A.创建和查找:msgget。int k=12;int qid=msgget(key,IPC_CREAT|0700);
B.发送:msgsnd。msgsnd(qid,&m,sizeof(m),0);m是自定义的通道中数据,最后的一个参数总是0。
C.接受:msgrcv。int no;no=cin(); msgrcv(qid,&m,sizeof(m),no,0);no是通道号。消息队列中的消息是一条一条的收取的。
D.删除:msgctl。msgctl(qid,IPC_RMID,NULL);返回<0,失败。
第二十天:
1.动态链接库:libxxx.so(UNIX,库名为xxx)、xxx.dll(WINDOWS),使用g++编译:g++ -l 库名 -L 库目录名。
2.动态链接库实例add.cc:
int add(int a,int b,int c)
{
int sum=a+b;int dif=sum-c;return dif;
}
头文件add.h:
#ifndef _ADD_H_
#define _ADD_H_
ing add(int a,int b,int c);
#endif
编译:g++ add.cc -shared -o libadd.so
使用:testadd.cc
int main()
{
int r=add(1,2,3);
}
调用:首先在环境变量中LD_LIBRARY_PATH中添加路径;
Export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:lib(动态链接库文件目录为lib)
g++ testadd.cc -ladd -Llib(lib是库文件目录)
3.OSI模型:应、表、会、传、网、数、物共七层
ISO/TCP/IP模型:应用层、传输层、网络接口层、物理层共5层
4.sockaddr_in结构:uint8-t sin_len(基本没用),sa_family_t sin_family(协议族AF_INET或者AF_INET6),in_prot_t sin_port(端口号),struct in_addr sin_addr(IP地址,IPV6中为in6_addr,in_addr结构为只有in_addr_t s_addr),char sin_zero[8]没使用。
5.字节序问题,大端格式、小端格式。解决方法:网络字节序、本地字节序。凡是传到网络的数据,一定转换成网络字节序。函数:
本地字节序->网络字节序:uit32_t htonl(uint32_t hostlong);或者unit16_t htons(ufint16_t hostshort);
网络字节序->本地字节序:unit32_t ntohl(unint32_t netlong);或者unit16_t ntols(ufint16_t netshort);
6.socket编程:
Server:socket->bind->listen->accept(连接后转到另一个socketA)->(SocketA)read/write->close(SocketA)
Client:socket->connect->read/write->close
int ss = socket(AF_INET,SOCK_STREAM,0);返回负值表示失败
int port = 12345;sockaddr_in si;si.sin_family=AF_INET;si.sin_port=htons(port);si.sin_addr.s_addr=或者IN_ADDR_ANY;
socklen_t len = sizeof(si);int r=bind(ss,(sockaddr*)&silen);r返回为负数就出错。只要使用套接字地址,一定有转换!
r=listen(ss,20);能够转接20个连接,r<0表示出错。
sockaddr_in c;for(;;){
len=sizeof(c);int cs=accept(ss,(sockaddr*)&c,&len);//客户端的ip地址存放在c中,len中存放长度。cs表示新产生的套接字,<0表示失败。套接字socket返回的就是一个整数。
//输出客户端地址,转换为点分十进制格式
char ip[100];inet_ntop(AF_INET,&c.sin_addr.s_addr,ip,sizeof(ip));
cout<
string msg = “your ip”;msg+=ip;msg+=’\n’;
write(cs,msg.c_str(),msg.size());
close(cs);
}//编译时man一下!得加命令参数,不同unix系统不一样