首页 > 代码库 > C++学习笔记(达内视频版)
C++学习笔记(达内视频版)
达内C++(陈宗权主讲)
第一天:
课程分为Core C++(标准C++。不依赖操作系统)和Unix C++。
1.配置bash,运行.sh文件。
vi bash_profile
在“path=”后面加入“:.”;path以“:”分隔路径,加入“.”表示当前文件夹可运行。
2.编程语言发展史:
二进制机器码->汇编(高效)->原始高级语言(Fortan等)->结构化编程语言(C语言等)->面向对象的编程语言(C++/Java等)
3.操作系统(Unix、Linux等)主要包含core和shell。
4.进程:一份正在执行的程序。
线程:一个程序能够由多个线程组成。栈:一小块内存空间 。堆:大片的内存空间。可自由申请。
5.编辑器(vi)、编译器(编译程序产生目标文件:g++ -c hello.cc)、连接器(将目标文件连接成可运行文件:g++ hello.o/g++ hello.o -o hell)、运行(a.out)。g++ hello.cc 编译+运行。g++ hello.cc -o hello编译+运行+重命名。
6.连接參数:-l连接指定的库文件。 -L指定库文件的路径。 -g产生带调试信息的可运行文件。
第二天:
1.C++中的字符、字符串:
char c[100] = “good\0day”; c中实际的内容为good。
2.四种类型转换:
static_cast、const_cast、dynamic_cast、reinterpret_cast
3.变量声明时规划一块内存空间。运行时真正分配内存空间。飞、使用sizeof计算类型占用内存空间。
4.定义常量:const double pi = 3.14; double const pi = 3.14;
第三天:
1.左移:全部二进制左移,右边空位补零,左側位丢失。
右移:全部二进制右移,右側位丢失,左側根据例如以下推断:假设数为无符号的,补0。假设数为符号数,非负数补0,负数补1。
2.对于无符号整数,最大值+1=最小值,最小值-1=最大值
3.enum color {RED,GREEN=200};color mycolor = RED;Enum用于定义类型;int a = 10;定义的是变量。使用cout输出枚举变量时输出的是整数值。枚举默认从0開始递增,也能够自己定义指定。
第四天:
1.打印小九九:
for(int i=1,j=1;i<=9&&j<=9;i++,j++)
{
cout<<i<<”*”<<j<<”=”<<i*j;
if(i<j)
{
cout<<(i*j>10? ’,’:’, ’;
}
else
{
cout<<’\n’;
j++;
i=0;
}
}
2.函数重载:同样函数名,不同參数类型、參数个数、參数顺序等。
3.函数參数带默认值:void show(char name[],bool g = true)。声明、实现都要写默认值。
函数的形參是复制了一份的变量。
4.inline内嵌函数:代码在调用的地方插入一份。
5.递归之汉诺塔:n个盘子能够先移动n-1个。然后移动最大的一个,然后将n-1移动到目标盘子。递归的思想。
void hano(int n,char a,char b,char c)//原始、暂时、目标位置
{
if(n==1)
{
cout<<a<<”=>”<<c<<endl;
}
else
{
hano(n-1,a,c,b);
hano(1,a,b,c);
hano(n-1,b,a,c);
}
}
6.递归之显示输入数字的每一位:(很好的理解递归的调用流程)
void show(int n)
{
if(n>=10)
{
show(n/10);
cout<<’ ’;
}
cout<<n%10;
}
7.三个文件:func.h用于函数声明、func.cc用于函数实现、main.cc用于主函数。编译步骤:
g++ -c main.cc
g++ *.o
a.out
或者:
g++ *.cc
编译器对每一个文件独立编译。
8.extern用于声明全局变量、函数,不分配内存空间。
Extern声明一般放到.h文件里,在.cc文件里定义并初始化。
static修饰静态变量、函数。仅仅能在本文件里使用。
第五天:
1.静态变量(静态局部变量、静态全局变量)的定义和初始化仅仅进行一次,然后保留变量值,知道程序结束。
静态函数仅仅能在本文件里使用。
2.数组大小C++标准要求是常量。而g++编译器对此不作要求。
3.int a[5];sizeof(a) = 20;sizeof计算的是类型的大小,尽管a代表的是数组的地址。
把函数名当成函数值用的时候,它只表示的是一个地址。
比如,数组名作为函数形參时,參数不是数组的内容,而是数组的地址。
4.特例:输出字符变量地址的时候,改成输出从这个地址開始存放的一系列字符。直到向后遇到第一个\0字符为止。
char ch[5] = {‘a‘,‘b‘,‘\0‘,‘c‘,‘d‘};
cout<<ch<<endl;输出的是ab
cout<<hex<<”0x”<<(int)ch<<endl;输出ch16进制地址
5.结构体定义:
typedef struct node
{
int value;
node *pNext;
}Node;
第六天:
1.引用输入的变量:
char* input(char* p)
{
cout<<”Input your name:”<<endl;
cin>>p;
return p;
}
2.指针在使用时必须初始化。指针仅仅能进行加减法操作。
3.指向指针的数组:int* pa[5];pa数组中存放的是指针;
指向数组的指针(指针数组):int (*a)[10];指针a指向的是含有十个元素的数组。
int a = 10;int b = 20;int *p = &a;int *q = &b;int *p1[] = {p,q};
cout<<p1[1]<<endl;cout<<*p1[1]<<endl;
p1是指向指针的数组,数组内存放的是指向int类型的指针;输出结果为:地址值,20;
int arr[] = {1,2,3};int (*p2)[3];p2 = &arr;cout<<(*p2)[1]<<endl;
p2是指向数组的指针,指向的数组是大小为3的int类型数组;输出结果为:2。
4.类型别名:
typedef int AA[10]; typedef定义元素类型,AA表示10个元素的int数组类型;
第七天:
1.*p++ <=> *(p++)
2.const int *p <=> int const *p :p称之为指向常量的指针。p指针指向的对象的值不能改变,p能够指向其它的对象。int* const p; p称为常指针,指向固定的内存地址。p指向变量的值能够改变。可是p不能够指向其它对象。
3.char *p = “good”; char *q = “bye”;strcat(p,q);错误。由于p指向的是字符串常量,字符串常量不能进行改动。
能够改成:char buf[] = “good”;char *q = “bye”; strcat(buf,q);
4.char buf[] = “hi”; char *p = “good”;sizeof(buf) = 3;sizeof(p) = 4;
5.main(int argc,char* argv[]):argc表示命令的个数,至少为1(命令名)。argv指针指向char类型的数组,也就是指向參数的字符串。
6.char *p = “Hello”;for(int i=0;i<strlen( p);i++){cout<<p+i<<endl;}
输出结果:Hello。ello,llo,lo,o;
7.引用是变量的别名,对引用的操作直接作用于被引用变量的本身。尽量使用引用来传递变量。尽量使用const来限制引用的參数。
8.函数指针:
void show(int a)
{
cout<<a<<endl;
}
int main()
{
void (*f)(int) ;
f = show;
(*f)(3);
第八天:
1.输出不等于现实。而是先经过缓冲区,最后现实。直接输出现实能够通过:cout<<”内容”<<flush实现。
2.class A
{
int n;
A():n(2){}//:后面表示初始化列表
}
第九天:
1.继承包含:单继承、多继承和虚继承。后两种非常少使用。
继承代表的是is-a关系,本质是代码的重用。并为多态提供前提。
组合是has-a的关系。公开继承原样不变,私有继承一律私有,保护继承公开变保护。
这里指的是派生类中基类的成员变量或者函数。
比如私有继承。派生类中的基类成员变量、函数的訪问类型是private,这样派生类的子类就不能訪问这些变量、函数了。
2.多态主要内容包含:虚函数、纯虚函数、类型识别和抽象类。
3.子类新建对象时:首先指向父类构造。然后运行自己的构造函数;子类析构对象时,首先调用自己的析构函数,最后调用父类的析构函数。
创建子类对象时,默认总是调用父类的无慘构造函数。假设须要父类接受子类含參构造函数。能够在子类初始化列表中用:父类名(參数)完毕父类含參构造函数的调用。
比如:Child(int a):a(0),Parent(0){};
4.有名对象:A a1(5);无名对象:A(2);无名对象一次性使用(A(2).show())。在运行完A(2)后马上释放,有名对象在作用域之后释放。
5.对于函数void show()和函数void show()const(不同意改动数据成员)。尽管參数列表没有不同。可是也构成重载,为const重载。对于对象A a1()中调用a1.show()。优先调用普通的void show();对于常量对象const A a2(),调用a2.show(),仅仅能调用的是void show()const。
6.多重继承对象的构造顺序依照继承列表进行。多重继承中通过作用域表示符解决多个父类中同名的现象。
7.虚继承用于解决继承的菱形结构问题(孙类含有两份父类变量),虚继承在孙类中仅仅保存虚基类一份内容。用法:class Base{};class derivedA:virtual Base{};class derivedB:virtual Base{};class child:public derivedA,public derivedB{};当中虚拟父类构造的參数在child中传递:child(參数):derivedA(參数),derivedB(參数),Base(參数)。
第十天:
1.char name[] = “Hello”;name[2] = “2”;能够运行
char *p = “Hello”;*p = ‘a’;不能运行,p指向的是常量字符串,不能改动。C++中使用string处理字符串,string是一个类,他不以\0作为结束标志。C中字符串使用\0作为结束标志。
2.子类对象总能够看成父类对象。多态的本质:C++中同意把派生类对象的地址赋给基类的指针,使用基类的指针调用不论什么方法,C++都能找到派生类的方法。C++中通过在父类方法中使用virtual实现多态。不加virtual调用的都是父类的方法。
3.指针、引用不是对象,它各自是对象的地址和别名。
通过指针和引用调用对象的方法时,依照其指向和代表的对象的类型来调用;Child c;Base b = c;b.show();调用父类的show。由于b的类型为Base,仅仅是使用Clild类型的c类初始化b。初始化过程中发生截断现象。而Base *b = &c;b->show();调用子类show,由于b是指针。指向的类型是Clild类型。
4.多态一定须要满足条件:1.继承关系2.调用的是虚函数3.訪问到的是对象自己(使用指针或者引用)
5.父类中函数仅仅是声明,不做实现的函数能够设置为纯虚函数。
Virtual void fun() = 0;含有纯虚函数的类称之为抽象类,抽象函数不能创建对象,仅仅能用来指 向或者引用子类对象。
6.(类型)数据=>强制类型转换;类型转换(四种)相比于强转而言,目的更加明白。类型转换格式:XXX_cast<目的类型>(待转换数据);类型转换不改变原类型!
A.static_cast:静态类型转换,用于:数值类型之间,有类型的指针和void* 类型之间。无名对象支持的转换。
B.const_cast:常量类型转换,用于:将常量转换成变量。Const int data; const_cast<int >(data);能够转,可是不改变data的类型,还是常量。不能改变其值。
须要改变能够这样:const_cast<int &>(data) = 1;
C.reinterpret_cast:又一次解释内存的转换。最危急的转换。
用于:不同类型指针之间的转换。
D.dynamic_cast:动态类型转换。子类转父类安全,父类转子类不一定,须要使用dynamic_cast。用于向下类型转换(父类转子类)。
dynamic_cast<子类指针>(父类地址)。
使用的是多态机制,要求父类至少有一个虚函数。
假设‘父类地址’指向的是子类对象,转换成功。返回地址;‘父类地址’指向的不是子类对象。转换失败。返回NULL。
7.class A{public:A(){};~A(){}} class B:public A{public:B(){};~B(){}}
假设调用A* p = new B;delete p;则实际调用的是A()->B()->~A();delete p时不调用~B();由于B的析构不是virtual。因此,能够将A的析构改为virtual ~A(){},这样调用delete p时调用:~B()->~A();一般父类析构函数都写成虚函数。
8.有元修饰符friend。包含有元函数和有元类,用于訪问类的私有成员、函数。
9.静态函数:仅本文件里可用。静态局部变量:将局部变量的声明周期延长到程序结束。一次初始化。
静态成员:类成员。全部类对象仅仅有一份。
第十一天:
1.构造对象一定调用构造函数,有时调用的可能是拷贝构造函数A(const A&)。
传递形參对象时,也产生一个暂时对象(调用拷贝构造构造函数,比如A add(A a1,A a2),a1,a2调用拷贝构造,add函数结束后析构。可是假设使用引用的话A& a1。就不会创建新对象这也是引用的优势。)。
调用拷贝构造函数的方式:A a1 = obj;或者A a2(obj)【可接受多个參数】;
2.拷贝构造函数格式:A(const A& o),一定不能为A(A o),这样创建时调用构造函数。调构造创建形參。形參又是新对象,创建新对象时又要调形參。。。没完没了,创建不了对象。
3.默认的拷贝构造韩式是浅复制。
4.重载运算符:将函数名换成operator 运算符。
比如:operator +。重载运算符和有元结合使用较多。
对于前++和后++。能够加入參数区分。
第十二天:
1.I/O包含cin/cout/ceer/clog。当中cerr和clog不缓冲,不能重定向。二cin和cout可以缓冲,可以重定向。
比如:cout<<”Hello”;cerr<<”world”;输出结果为“wordHello”。由于cerr直接输出。cout须要缓冲。最后才输出。
2.文件输入输出使用ifstream和ofstream。
比如:
ofstream fout(“a.out”);fout<<”Hello”<<endl;fout<<12.34<<endl;fout.close();fout.write(地址,字节数);将一块内存空间中的字节写到文件里。
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<<tree->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->data<tree->data){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(t<tree->data){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<typename T>
void disp(T* a, int n)
{
for(int i=0;i<n;i++){cout<<a[i];}
}
系统调用时自己主动匹配类型。
系统优先选择非模板函数。使用模板尽量声明和定义不分开。
模板保存在头文件里。
类模板:
template<typename T>
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、显示当前usernamewhoami
3.显示全部环境变量:
int main(int ac, char* av[], char* env[])
{
for(int i=0;env[i]!=NULL;i++){cout<<env[i]<<endl;}
}
获取环境变量值:getenv(name);使用man getenv查看需包括头文件。
环境变量设置:char* var = “class=3”;putenv(var);仅在本进程及其子进程有效。
4.进程的状态:O进程正在进行、S进程等待cpu调度、R进程准备完成但尚未运行、T进程被挂起、Z僵死进程
5.Include<pwd.h>、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<<p->tm_year+1990<<”年”<<p->tm_mon+1<<”月”<<p->tm_mday<<”日 星期”<<p->tm_wday<<” ”<<p->tm_hour<<”:”<<p->tm_min<<”:”<<p->tm_sec<<endl;
strftime();使用更简洁,电子时钟:
for(;;)
{
time_t t = time(NULL); tm* p = localtime(&t); char buf[100];
strftime(buf,100,”%F 星期%W %T”,p);
cout<<’\r’<<buf<<flush;
//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使用时easy产生父进程、子进程代码混在一起的现象。由于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”<<endl;}//fd表示文件标示符
cout<<”Pipe ready!”<<endl;
while(1)
{
cout<<”input text:”<<endl; string str; getline(cin,str);
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<<buf<<endl;if(strcmp(buf,”bye”)==0){break;}
*/
}
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);//client的ip地址存放在c中,len中存放长度。cs表示新产生的套接字,<0表示失败。套接字socket返回的就是一个整数。
//输出client地址,转换为点分十进制格式
char ip[100];inet_ntop(AF_INET,&c.sin_addr.s_addr,ip,sizeof(ip));
cout<<ip<<”到此一游”<<endl;
string msg = “your ip”;msg+=ip;msg+=’\n’;
write(cs,msg.c_str(),msg.size());
close(cs);
}//编译时man一下!得加命令參数。不同unix系统不一样
C++学习笔记(达内视频版)