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