v 題型:前端
數據結構(鏈表,數,圖)、C++編程基礎、數組、遞歸、矩陣、內存管理、時間複雜度、TCP/IP、操做系統、計算機網絡、UML、OOA&OOP、本身的項目程序員
v swap的幾種寫法:算法
void swap(int* a, int* b)編程 |
||
{ int temp;數組 temp = *a;安全 *a = *b;網絡 *b = temp; }數據結構 |
{ *a = *a + *b;app *b = *a - *b;異步 *a = *a - *b; } |
{ *a = *a ^ *b; *b = *b ^ *a; *a = *a ^ *b; } |
v 如何在C++中調用C程序:
C++和C是兩種徹底不一樣的編譯連接處理方式,若是直接在C++裏面調用C函數,會找不到函數體,報連接錯誤。要解決這個問題,就要在 C++文件裏面顯示聲明一下哪些函數是C寫的,要用C的方式來處理。
1.引用頭文件前須要加上 extern 「C」,若是引用多個,那麼就以下所示
extern 「C」
{
#include 「 s.h」
#include 「t.h」
#include 「g.h」
#include 「j.h」
};
而後在調用這些函數以前,須要將函數也所有聲明一遍。
2.C++調用C函數的方法,將用到的函數所有從新聲明一遍
extern 「C」
{
extern void A_app(int);
extern void B_app(int);
extern void C_app(int);
extern void D_app(int);
}
C++程序中調用被c編譯器編譯後的函數,爲何要加extern "C"?
C++語言支持函數重載,C語言不支持函數重載。函數被C++編譯後在庫中的名字與C語言的不一樣。假設某個C 函數的聲明以下:
void foo(int x, int y);
該函數被C 編譯器編譯後在庫中的名字爲_foo,而C++編譯器則會產生像_foo_int_int之類的名字用來支持函數重載和類型安全鏈接。因爲編譯後的名字不一樣,C++程序不能直接調用C 函數。C++提供了一個C 鏈接交換指定符號extern「C」來解決這個問題。例如:
extern 「C」
{
void foo(int x, int y);
// 其它函數
}
或者寫成
extern 「C」
{
#include 「myheader.h」
// 其它C 頭文件
}
這就告訴C++編譯譯器,函數 foo 是個C 鏈接,應該到庫中找名字_foo 而不是找_foo_int_int。C++編譯器開發商已經對C 標準庫的頭文件做了extern「C」處理,因此咱們能夠用#include直接引用這些頭文件。
v 操做符優先級
優先級 |
運算符 |
名稱或含義 |
使用形式 |
結合方向 |
說明 |
1 |
[] |
數組下標 |
數組名[常量表達式] |
左到右 |
|
() |
圓括號 |
(表達式)/函數名(形參表) |
|||
. |
成員選擇(對象) |
對象.成員名 |
|||
-> |
成員選擇(指針) |
對象指針->成員名 |
|||
2 |
- |
負號運算符 |
-表達式 |
右到左 |
單目 |
(類型) |
強制類型轉換 |
(數據類型)表達式 |
|||
++ |
自增運算符 |
++變量名/變量名++ |
單目 |
||
-- |
自減運算符 |
--變量名/變量名-- |
單目 |
||
* |
取值運算符 |
*指針變量 |
單目 |
||
& |
取地址運算符 |
&變量名 |
單目 |
||
! |
邏輯非運算符 |
!表達式 |
單目 |
||
~ |
按位取反運算符 |
~表達式 |
單目 |
||
sizeof |
長度運算符 |
sizeof(表達式) |
|||
3 |
/ |
除 |
表達式/表達式 |
左到右 |
雙目 |
* |
乘 |
表達式*表達式 |
雙目 |
||
% |
餘數(取模) |
整型表達式/整型表達式 |
雙目 |
||
4 |
+ |
加 |
表達式+表達式 |
左到右 |
雙目 |
- |
減 |
表達式-表達式 |
雙目 |
||
5 |
<< |
左移 |
變量<<表達式 |
左到右 |
雙目 |
>> |
右移 |
變量>>表達式 |
雙目 |
||
6 |
> |
大於 |
表達式>表達式 |
左到右 |
雙目 |
>= |
大於等於 |
表達式>=表達式 |
雙目 |
||
< |
小於 |
表達式<表達式 |
雙目 |
||
<= |
小於等於 |
表達式<=表達式 |
雙目 |
||
7 |
== |
等於 |
表達式==表達式 |
左到右 |
雙目 |
!= |
不等於 |
表達式!= 表達式 |
雙目 |
||
8 |
& |
按位與 |
表達式&表達式 |
左到右 |
雙目 |
9 |
^ |
按位異或 |
表達式^表達式 |
左到右 |
雙目 |
10 |
| |
按位或 |
表達式|表達式 |
左到右 |
雙目 |
11 |
&& |
邏輯與 |
表達式&&表達式 |
左到右 |
雙目 |
12 |
|| |
邏輯或 |
表達式||表達式 |
左到右 |
雙目 |
13 |
?: |
條件運算符 |
表達式1? 表達式2: 表達式3 |
右到左 |
三目 |
14 |
= |
賦值運算符 |
變量=表達式 |
右到左 |
|
/= |
除後賦值 |
變量/=表達式 |
|||
*= |
乘後賦值 |
變量*=表達式 |
|||
%= |
取模後賦值 |
變量%=表達式 |
|||
+= |
加後賦值 |
變量+=表達式 |
|||
-= |
減後賦值 |
變量-=表達式 |
|||
<<= |
左移後賦值 |
變量<<=表達式 |
|||
>>= |
右移後賦值 |
變量>>=表達式 |
|||
&= |
按位與後賦值 |
變量&=表達式 |
|||
^= |
按位異或後賦值 |
變量^=表達式 |
|||
|= |
按位或後賦值 |
變量|=表達式 |
|||
15 |
, |
逗號運算符 |
表達式,表達式,… |
左到右 |
從左向右順序運算 |
v ::在C++中是什麼意思
::是運算符中等級最高的,它分爲三種:
1)global scope(全局做用域符),用法(::name)
2)class scope(類做用域符),用法(class::name)
3)namespace scope(命名空間做用域符),用法(namespace::name)
他們都是左關聯(left-associativity)
他們的做用都是爲了更明確的調用你想要的變量,如在程序中的某一處你想調用全局變量a,那麼就寫成::a,若是想調用class A中的成員變量a,那麼就寫成A::a,另一個若是想調用namespace std中的cout成員,你就寫成std::cout(至關於using namespace std;cout)意思是在這裏我想用cout對象是命名空間std中的cout(即就是標準庫裏邊的cout)
v i++
!x++; !與++優先級相同,先計算!x;而後計算x++; (原題:P35)
v 指針+1的問題
指針變量加1,即向後移動1 個位置表示指針變量指向下一個數據元素的首地址。而不是在原地址基礎上加1。至於真實的地址加了多少,要看原來指針指向的數據類型是什麼。
p指向的是一個字符,p+1就是移動一個字符大小,一個字符就是一個字節,因此p +1 表明的地址就比 p 表明的地址大1。
p指向的是一個整型,p+1就是移動一個整型大小,即移動4個字節,因此p+1表明的地址比p表明的地址大4.
v x++與++x
#include <stdio.h> void main() { int arr[] = { 6, 7, 8, 9, 10 }; int *ptr = arr; *(ptr++) += 123;//equal:*ptr=*ptr+123;ptr++; printf("%d,%d\n",*ptr,*(++ptr));//prinft計算參數時從右向左入棧,先執行++ptr; } RESULT:8,8 X++先執行運算最後++;++X則先++而後執行其餘運算; |
v 隱式類型轉換髮生在下列這些典型的狀況下:
v 判斷一個數X是不是2N次方,不可用循環語句:
!(X&(X-1))==0
v Const
在C程序中,const的用法主要是定義常量、修飾函數參數、修飾函數返回值,在C++中,它還能夠修飾函數的定義體,定義類中某個成員函數爲恆態函數,即不改變類中的數據成員
被Const修飾的東西能夠受到強制保護,預防意外的變更,能提升程序的健壯性
Const與#define相比有什麼不一樣?
Const常量有數據類型,而宏常量沒有數據類型,編譯器能夠對前者進行類型安全檢查,而對後者只能進行字符替換,沒有類型安全檢查,而且在字符替換中可能產生意料不到的錯誤
調試工具能對const常量進行調試,可是沒法對define常量調試
No. |
做用 |
說明 |
參考代碼 |
1 |
能夠定義const常量 |
|
const int Max = 100; |
2 |
便於進行類型檢查 |
const常量有數據類型,而宏常量沒有數據類型。編譯器能夠對前者進行類型安全檢查,而對後者只進行字符替換,沒有類型安全檢查,而且在字符替換時可能會產生意料不到的錯誤 |
void f(const int i) { .........} |
3 |
能夠保護被修飾的東西 |
防止意外的修改,加強程序的健壯性。 |
void f(const int i) { i=10;//error! } |
4 |
能夠很方便地進行參數的調整和修改 |
同宏定義同樣,能夠作到不變則已,一變都變 |
|
5 |
爲函數重載提供了一個參考 |
|
class A |
6 |
能夠節省空間,避免沒必要要的內存分配 |
const定義常量從彙編的角度來看,只是給出了對應的內存地址,而不是象#define同樣給出的是當即數,因此,const定義的常量在程序運行過程當中只有一份拷貝,而#define定義的常量在內存中有若干個拷貝 |
#define PI 3.14159 //常量宏 |
7 |
提升了效率 |
編譯器一般不爲普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成爲一個編譯期間的常量,沒有了存儲與讀內存的操做,使得它的效率也很高 |
|
const int *A; 或 int const *A; //const修飾指向的對象,A可變,A指向的對象不可變
int *const A; //const修飾指針A, A不可變,A指向的對象可變
const int *const A; //指針A和A指向的對象都不可變
v sizeof與Strlen()
sizeof 單位:字節
sizeof(...)是運算符,而不是一個函數。一個簡單的例子:
int a;
cout<<sizeof a<<endl;
在頭文件中typedef爲unsigned int,其值在編譯時即計算好了,參數能夠是數組、指針、類型、對象、函數等。
它的功能是:得到保證能容納實現所創建的最大對象的字節大小。
因爲在編譯時計算,所以sizeof不能用來返回動態分配的內存空間的大小。
實際上,用sizeof來返回類型以及靜態分配的對象、結構或數組所佔的空間,返回值跟對象、結構、數組所存儲的內容沒有關係。
具體而言,當參數分別以下時,sizeof返回的值表示的含義以下:數組——編譯時分配的數組空間大小;
指針——存儲該指針所用的空間大小(存儲該指針的地址的長度,是長整型,應該爲4);類型——該類型所佔的空間大小;
對象——對象的實際佔用空間大小;
函數——函數的返回類型所佔的空間大小。函數的返回類型不能是void。
********************************************************************
strlen
strlen(...)是函數,要在運行時才能計算。
參數必須是字符型指針(char*), 且必須是以'\0'結尾的。當數組名做爲參數傳入時,實際上數組就退化成指針了。
int ac[10];
cout<<sizeof(ac)<<endl;
cout<<strlen(ac)<<endl; (ac至關於一個指針,可是strlen只能接受char*類型,因此編譯時出錯)
它的功能是:返回字符串的長度。該字符串多是本身定義的,也多是內存中隨機的,該函數實際完成的功能是從表明該字符串的第一個地址開始遍歷,直到遇到結束符'\0'。返回的長度大小不包括'\0'。
v 結構體長度對齊
在默認狀況下,爲了方便對結構體內元素的訪問和管理,當結構體內的元素長度都小於處理器的位數的時候,便以結構體裏面最長的數據元素爲對其單位,也就是說,結構體的長度必定是最長的數據元素的整數倍;若是結構體內存在長度大於處理器位數的元素,那麼就以處理器的位數爲對齊單位。可是結構體內類型相同的連續元素將在連續的空間內,和數組同樣。
CPU的優化規則大體是這樣:對於n字節的元素(n=2,4,8,…),他的首地址能被n整除,才能得到最好的性能。(我的感受是若是不是這樣,那麼一個元素頗有可能會跨頁存儲,存在兩個不一樣的頁面上,致使訪問速度慢)
v 對其是一種以空間換時間的方法,在訪問內存時,若是地址按4字節對齊,則訪問效率會高不少,這種現象的緣由在於訪問內存的硬件電路。通常狀況下,地址總線老是按照對齊後的地址來訪問的。例如你想獲得0x00000001開始的4字節內容,系統需先以0x00000000讀4個字節,從中取3字節,而後再用0X00000004最爲開始地址,得到下一個四字節,再從中獲得第一個字節,兩次組合出你想要的內容。可是若是地址一開始就是對齊到0x00000000,則系統只需一次內存讀寫便可。
v 對其的自定義設置
#pragma pack(1)
struct aStruct…{…};
#pragma pack()
v 字節對齊是在編譯時決定的,不會再發生變化,在運行時不會再發生變化
v 靜態變量存放在全局數據區,而sizeof計算棧中分配的大小,是不會計算static變量的。
v 數據類型的長度:
short -------------- 2
int,long,float,指針---4
double -------------- 8
v sizeof vs strlen
sizeof是算符,strlen是函數
sizeof能夠用類型作參數,strlen只能用char*作參數,且必須是以「\0」結尾的。
數組作sizeof的參數不退化,傳遞給strlen就退化爲了指針
sizeof在編譯時計算,strlen在運算時計算
對函數使用sizeof,在編譯階段會被函數返回值類型取代。
v sizeof不是函數,也不是一元運算符,他是個相似宏定義的特殊關鍵字,sizeof()括號內的內容是不被編譯的,只是替換,因此a=8;sizeof(a=6);以後a的值仍然爲8。
v unsigned影響的僅僅是最高位bit的意義(正/負),而不會影響數據長度。
v 空類所佔空間爲1,單一繼承的空類空間也爲1,多重繼承的空類空間仍是1,可是虛繼承涉及到虛表(虛指針),因此空間大小爲4
v 內聯函數和宏定義
內聯函數和普通函數相比能夠加快程序運行的速度,由於不須要中斷調用,在編譯的時候內聯函數能夠直接被鑲嵌到目標代碼中,而宏只是一個簡單的替換;內聯函數要作參數類型檢查,這是inline和宏相比的優點;
inline通常只用於以下狀況:
—————————————————————————————————————————————————————————————————————————————————————————
v 指針問題:包括常量指針、數組指針、函數指針、this指針、指針傳值、指向指針的指針等問題
v 指針和引用的區別
非空區別:在任何狀況下都不能使用指向空值的引用;
合法性區別:在使用引用以前不須要測試它的合法性;
可修改區別:指針能夠被從新賦值以指向另外一個不一樣的對象,可是引用則老是指向初始化時被指定的對象,之後不能改變,可是指定的內容能夠改變;
應用區別:使用指針:存在不指向任何對象的可能;須要 在不一樣的時刻指向不一樣的對象;使用引用:老是指向一個對象而且一旦指向一個對象後就不會改變指向。
v 引用的初始化:
聲明一個引用時,必須同時初始化,如同const常量必須聲明的同時初始化。
v char c[] vs char *c
char c[]分配的是局部數組,而char *c分配的是全局數組
v 函數指針
函數指針 |
void (*f)() |
函數返回指針 |
void* f() |
const指針 |
cons int * |
指向const的指針 |
int * const |
指向const的const指針 |
cons int* const |
v malloc和free是C/C++的標準庫函數,new/delete是C/C++的運算符。他們均可以用於動態申請內存和釋放內存。對於非內部數據類型的對象而言,光用malloc/free沒法知足動態對象的要求。對象在建立的同時要自動執行構造函數,對象在消亡以前要自動執行析構函數。因爲malloc/free是庫函數而不是運算符,不在編譯器控制權限範圍內,不能把執行構造函數和析構函數的任務強加於malloc/free
v 數組指針和指針數組:
數組指針 |
指針數組 |
int (*a)[] |
int *a[] |
—————————————————————————————————————————————————————————————————————————————————————————
v STL模版與容器
_________________
v STL的優勢:
v STL中的一些基本概念
v 容器向量:標準模板庫是一個基於模版的容器類庫,包括鏈表、列表、隊列和堆棧;標準模板庫還包括許多經常使用的算法,包括排序和查找;可重用;容器是包括其餘對象的對象;標準模版庫類有兩種類型:順序和關聯;不一樣OS間可移植;
v vector的操做:
標準庫vector類型使用須要的頭文件:#include <vector>。vector 是一個類模板。不是一種數據類型,vector<int>是一種數據類型。Vector的存儲空間是連續的,list不是連續存儲的。
初學C++的程序員可能會認爲vector的下標操做能夠添加元素,其實否則:
vector<int> ivec; // empty vector
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec[ix] = ix; // disaster: ivec has no elements
上述程序試圖在ivec中插入10個新元素,元素值依次爲0到9的整數。可是,這裏ivec是空的vector對象,並且下標只能用於獲取已存在的元素。
這個循環的正確寫法應該是:
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec.push_back(ix); // ok: adds new element with value ix
警告:必須是已存在的元素才能用下標操做符進行索引。經過下標操做進行賦值時,不會添加任何元素,僅能對確知已存在的元素進行下標操做 。
v 泛型編程
何謂泛型編程:STL表明用一致的方式編程是可能的;泛型編程是一種基於發現高效算法的最抽象表示的編程方法;STL是一個泛型編程的例子,C++是咱們能夠實現使人信服的例子的語言。
v 模版
一個函數在編譯時被分配給一個入口地址,這個入口地址就稱爲函數的指針,正如指針是一個變量的地址同樣。(經測試:函數名和對函數名取地址獲得的是同一個值,均可以做爲該函數的指針)。有些地方必須使用函數指針才能完成給定的任務,特別是異步操做的回調和其餘須要匿名回調的結構。另外,想線程的執行和時間的處理,若是缺乏了函數的支持也是很難完成的。
—————————————————————————————————————————————————————————————————————————————————————————————
v 面向對象(Object-Oriented)
編程是在計算機中反應世界
面向對象的優勢:良好的可複用性、易維護、良好的可擴充性
面向對象技術的基本概念是:對象、類和繼承
C++中的空類默認產生的成員函數:
默認構造函數、析構函數、拷貝構造函數、賦值函數
C++中struct也能夠有constructor/destructor及成員函數,它和class的區別是:class的默認訪問控制是private,struct中默認訪問控制是public。
在一個類中,初始化列表的初始化變量順序是根據成員變量的聲明順序來執行的。
MFC庫中,爲何CObject的析構函數是虛函數?
保證在任何狀況下,不會出現因爲析構函數未被調用而致使內存泄漏
析構函數能夠是內聯函數,但內聯函數不能爲virtual
多態:容許將子類類型的指針賦值給父類類型的指針,多態在Object Pascal和C++中都是經過虛函數(Virtual Function)實現的。
實現代碼重用 |
封裝:隱藏實現細節,使得代碼模塊化
繼承:擴展已存在的代碼模塊(類)
多態:同一操做做用於不一樣對象上,產生不一樣的解釋和執行結果—實現接口重用
友元函數:定義在類外部的普通函數,但他須要在類體內說明,爲了與該類的成員函數區別,說明時須要在前面加上friend來區別;他雖然不是成員函數,但卻能夠訪問類的私有變量。
友元還能夠是類,稱爲友元類
繼承:可使一個新類得到其父類的操做和數據結構,程序員只需在新類中增長原有類中沒有的成分。
爲了避免破壞類的封裝性,全部操做應該經過接口進行溝通。
類中的static變量存在數據段,不在類的實例空間中。