首页 > 代码库 > 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++学习笔记(达内视频版)