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系統不一樣


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM