C++學習筆記(達內視頻版)

達內C++(陳宗權主講)node

第一天:ios

課程分爲Core C++(標準C++。不依賴操做系統)和Unix C++。算法

1.配置bash,運行.sh文件。shell

vi bash_profile編程

在「path=」後面加入「:.」;path以「:」分隔路徑,加入「.」表示當前文件夾可運行。數組

2.編程語言發展史:安全

二進制機器碼->彙編(高效)->原始高級語言(Fortan等)->結構化編程語言(C語言等)->面向對象的編程語言(C++/Java等)bash

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系統不同

相關文章
相關標籤/搜索