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<

if(i

{

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<”<

}

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<

}

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<

cout<

5.结构体定义:

typedef struct node

{

int value;

node *pNext;  

}Node;

第六天:

1.引用输入的变量:

char* input(char* p)

{

cout<<”Input  your name:”<

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是指向指针的数组,数组内存放的是指向int类型的指针;输出结果为:地址值,20;

int arr[] = {1,2,3};int (*p2)[3];p2 = &arr;cout<<(*p2)[1]<

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

输出结果:Hello,ello,llo,lo,o;

7.引用是变量的别名,对引用的操作直接作用于被引用变量的本身。尽量使用引用来传递变量,尽量使用const来限制引用的参数。

8.函数指针:

void show(int a)

{

cout<

}

int main()

{

void (*f)(int) ;

f = show;

(*f)(3);

第八天:

1.输出不等于现实,而是先经过缓冲区,最后现实。直接输出现实可以通过:cout<<”内容”<

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(data);可以转,但是不改变data的类型,还是常量,不能改变其值。需要改变可以这样:const_cast&>(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”<

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系统不一样

你可能感兴趣的:(C/C++/VC)