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

  • 相关阅读:
    题解-CF1097F Alex and a TV Show
    题解-UVA757 Gone Fishing
    题解-UVA1401 Remember the Word
    题解-UVA11732 "strcmp()" Anyone?
    【数学】[AH2017/HNOI2017]礼物
    Educational Codeforces Round 109
    KYOCERA Programming Contest 2021(AtCoder Beginner Contest 200)
    Codeforces Global Round 14
    CF906E Reverses(H)
    【LuoguP4464】 [国家集训队] JZPKIL(M)
  • 原文地址:https://www.cnblogs.com/llguanli/p/7183257.html
Copyright © 2020-2023  润新知