zoukankan      html  css  js  c++  java
  • 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] = “goodday”; 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<<’ ’;

    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.特例:输出字符变量地址的时候,改成输出从这个地址開始存放的一系列字符。直到向后遇到第一个字符为止。

    char ch[5] = {'a','b','','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是一个类,他不以作为结束标志。C中字符串使用作为结束标志。

    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);此外能够指定结束符,默觉得 。getline(in,str,’ ’);

    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加入 并清空缓冲区、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<<’ ’<<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 ”,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]=’ ’;

        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+=’ ’;

    write(cs,msg.c_str(),msg.size());

    close(cs);

    }//编译时man一下!得加命令參数。不同unix系统不一样

  • 相关阅读:
    UVa 1354 天平难题 (枚举二叉树)
    广西邀请赛总结
    UVa 12118 检查员的难题 (dfs判连通, 构造欧拉通路)
    UVA
    Uva 127 "Accordian" Patience (模拟)
    UVA 10539 Almost Prime Numbers( 素数因子)
    HDU 1272 小希的迷宫(并查集)
    HDU 1213 How Many Tables (并查集)
    POJ 2236 Wireless Network(并查集)
    HDU 1233 还是畅通工程 ( Kruskal或Prim)
  • 原文地址:https://www.cnblogs.com/llguanli/p/7183257.html
Copyright © 2011-2022 走看看