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