1. 程式人生 > >【C++程式設計】1.從c走進c++

【C++程式設計】1.從c走進c++

1.函式指標

    (1)基本概念:程式執行期間,每個函式都會佔用一段連續的記憶體空間。而函式名就是該函式所佔記憶體區域的起始位置(也稱入口地址)。我們可以將一個函式的入口地址賦值給一個指標變數,使該指標變數指向該函式。然後通過指標變數就可以呼叫這個函式。這種指向函式的指標被稱為函式指標。

    (2)函式指標定義形式:

        型別名(*指標變數名)(引數型別1,引數型別2,....)

        例如:int(*pf)(int,char);         表示pf是一個函式指標,它所指向的函式返回值應該是int,該函式應有兩個引數,第一個是Int型別,第二個是char型別。

    (3)使用方法:可以用一個原型匹配的函式的名字給一個函式指標賦值。要通過函式指標呼叫它所指向的函式,寫法為:

            函式指標名(實參表);

            例:

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
void PrinMin(int a, int b) {
	if (a < b)
		cout << a << endl;
	else
		cout << b << endl;
}
int main() {
	void(*pf)(int, int);
	int x = 4, y = 5;
	pf = PrinMin;
	pf(x, y);
	getchar();
	return 0;
}

    (4)函式指標和qsort庫

        void qsort(void*base,int nelem,snsigned int width,int(*pfCompare)(const void*,const void*));

        可以對任意型別的陣列進行排序

        對陣列排序,需要知道:

        (1)陣列起始地址

        (2)陣列元素個數

        (3)每個元素的大小(由此可以算出每個元素的地址)

        (4)元素誰在前誰在後的規則

        base:待排序陣列的起始地址,nelem:待排序陣列元素個數,width:待排序陣列的每個元素的大小(以位元組為單位)

        pfCompare:比較函式的地址。比較函式是自己編寫的。

        排序就是一個不斷比較並交換位置的過程。qsort函式在執行期間,會通過pfCompare指標呼叫“比較函式”,呼叫時將要比較的兩個元素的地址傳給“比較函式”,然後根據“比較函式”返回值判斷兩個元素哪個更應該排在前面。

        比較函式編寫規則:int 比較函式名(const void*elem1,const void* elem2)

        a.如果*elem1應該排在*elem2前面,則函式返回值是負整數。

        b.如果*elem1和*elem2哪個排在前面都行,那麼函式返回0

        c.如果*elem1應該排在*elem2後面,則函式返回值是正整數

        例程:將數字按個位數從小到大排序

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
int MyCompare(const void*elem1, const void*elem2)
{
	unsigned int*p1;
	unsigned int*p2;
	p1 = (unsigned int*)elem1;
	p2 = (unsigned int*)elem2;
	return *p1 % 10 - *p2 % 10;
}

#define NUM 5
int main()
{
	unsigned int a[NUM] = { 5,9,13,14,28 };
	qsort(a, NUM, sizeof(unsigned int), MyCompare);
	for (int i = 0; i < NUM; i++)
		cout << a[i]<<endl;
	system("pause");
	getchar();
	return 0;
}

2.命令列引數

    int main(int argc,char* argv[])

    {

        ........

    }

    argc:代表啟動程式時,命令列引數的個數。C/C++語言規定,可執行程式程式本身的檔名,也算一個命令列引數,因此argc的值至少是1.

    argv:指標陣列,其中每一個元素都是一個char*型別的指標,該指標指向一個字串,這個字串裡就存放著命令列引數。

            例如,argv[0]指向的字串就是第一個命令列引數,即可執行程式的檔名,argv[1]指向第二個命令列引數,argv[2]指向第三個命令列引數。

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
int main(int argc, char*argv[])
{
	for (int i = 0; i < argc; i++)
		cout << argv[i] << endl;
	return 0;
}

    將上面的程式編譯成sample.exe,然後在控制檯視窗敲:

    sample para1 pare2 s.txt5 "hello world"

3.位運算

    有時候需要對某個整數型別變數中的某一位進行操作,例如,判斷某一位是否為1或只改變其中某一位而保持其他位都不變。C++語言提供了6種位運算子來進行“位運算”操作:按位與(&)、按位或(|)、按位異或(^)、取反(~)、左移(<<)、右移(>>)。

4.引用的概念和應用

    (1)引用定義的方式如下:

        型別名&引用名=同類型的變數名。

    某個變數的引用和這個變數是一回事,相當於該變數的一個別名。注意:

        1.定義引用時一定要將其初始化,否則編譯不會通過。通常會用某個變數去初始化引用。

        2.初始化後,它就一直引用該變數,不會再引用其他變量了。也可以用一個引用去初始化另一個引用,這樣兩個引用就引用同一個變量了。

        3.引用只能引用變數,不能用常量初始化引用。

    (2)引用的應用

    在C語言中,編寫兩個整形變數值交換的函式;

void swap(int a,int b)
{ int tmp;
  tmp=a;a=b;b=tmp
}
int n1,n2;
swap(n1,n2)

    在C語言中,這樣寫變數的值不會被交換,因為函式裡改變形參的值不會影響到實參。

    C語言中正確的寫法:

void swap(int* a,int* b)
{ int tmp;
  tmp=*a;*a=*b;*b=tmp
}
int n1,n2;
swap(&n1,&n2)

    C++裡,可以應用引用:

void swap(int& a,int& b)
{ int tmp;
  tmp=a;a=b;b=tmp
}
int n1,n2;
swap(n1,n2)

    這樣寫變數的值會改變,因為int& a表示引用,引用後兩個變數是等價的,a等價於n1,b等價於n2,改變a的值n1也會跟著變。

    (3)常引用

    定義引用時,可以在前面加“const”關鍵字,則該引用就成為“常引用”。

    如: int n;

            const int&r=n;

    常引用和普通引用的區別在於不能通過常引用去修改其引用的內容。注意,不是常引用所引用的內容不能被修改,而是不能通過常引用修改其引用的內容。如:

    int n=100;

    const  int&r=n;

    r=200;                //編譯出錯,不能通過常引用修改其引用的內容

    n=300;                //沒問題,n的值變為300.

        注意:const T&和T&是不同的型別。

        (1)T&型別的引用或T型別和變數可以用來初始化const T&型別的引用。

        (2)const& T型別的常變數和const T&型別的引用則不能用來初始化T&型別的引用,除非進行強制型別轉換。

5.const關鍵字的用法

    (1)定義常量。定義變數時如果在型別名前面加上“const”關鍵字,該變數就變為“常變數”

        const int a=5.

        常變數的值只能用初始化的方式給出,此後不能被修改。對常變數賦值會導致編譯出錯。

    (2)定義指標。定義指標的時候,可以在前面加const關鍵字,則該指標就成為“常量指標”。

        const T* P;

            常量指標和普通指標的區別在於:不能通過常量指標去修改其指向的內容。注意,不是常量指標所指向的內容不能被修改,只是不能通過常量指標修改其指向的內容,可以用別的辦法修改。   

const int* p;
int n=100;
p=&n;
*p=200;        //編譯出錯,不能通過常量指標修改其指向的內容。
n=300;         //編譯正確,n的值變為300

        注意:const T*和T*是不同的型別。T*型別的指標可以賦值給const T*型別的指標,反過來不行,除非進行強制型別轉換。

        函式引數為常量指標時,可避免函式內部不小心改變函式引數指標所指地方的內容。

6.動態記憶體分配

   【1】. C++裡可以用new運算子實現動態記憶體分配,使得程式可以在執行期間,根據實際需要,要求作業系統臨時分配給自己一片記憶體空間用於存放資料。此種記憶體分配是在程式執行中執行的,而不是在編譯時就確定的,因此稱為“動態記憶體分配”。new運算子的兩種用法如下:

    (1)P=new T;

        其中,T是任意型別名,P是型別為T*的指標。動態分配出一片大小為sizeof(T)位元組的記憶體空間,並且將該記憶體空間的起始地址賦值給P。例如:

        int *P;

        p=new int;

        *p=5;

    (2)P= new T[N]

        其中,T是任意型別名;P是型別為T*的指標;N代表“元素個數”,可以是任何值為正整數的表示式,表示式中可以包含變數、函式呼叫等。這樣的語句動態分配出N*sizeof(T)個位元組的記憶體空間,這片空間的起始地址被賦值給P。例如:

        int *pn;

        int i=5;

        pn=new int[i*20]

        pn[0]=20;

        pn[100]=30'

        最後一行編譯時沒問題,但執行時會導致陣列越界。

    注意:new 運算子返回值的型別:new T ,new T[N] 返回值的型別都是T*。

   【2】C++裡用delete運算子釋放動態分配的記憶體,new出來的動態記憶體空間一定要用delete運算子進行釋放。用法:

       (1) delete 指標;  //該指標必須是指向動態分配的記憶體空間的,否則執行時很可能會出錯。例如:

        int *p=new int;

        *p=5;

        delete p;

        delete p;    //本句會導致程式出錯 因為p指向的空間已經釋放了,p不再是指向動態分配的記憶體空間的指標了。

        (2) delete[ ]指標;

           例如:

           int *p=new int[20];

           p[0]=1;

            delete[ ]p;

            同樣要求,被釋放的指標p必須是指向動態分配的記憶體空間的指標,否則會出錯。

            !注意:如果動態分配了一個數組,但是卻用“delete指標”的方式釋放,沒有用[ ],則編譯時沒有問題,執行時也一般不會發生錯誤,但實際上會導致動態分配的陣列沒有被完全釋放。

7.行內函數

    函式呼叫是有時間開銷的,如果函式內部本身就沒有幾條語句,執行的時間本來就很短,而且函式被反覆執行很多次,相比之下,呼叫函式所產生的開銷就比較大。

    為了減少函式呼叫的開銷,引入了行內函數機制,編譯器處理對行內函數的呼叫語句時,是將整個函式的程式碼插入到呼叫語句處,而不會產生呼叫函式的語句。

    行內函數的寫法:在定義函式時,在返回值型別前面加上“inline”關鍵字。例如:

        inline int Max(int a,int b)

        {

                if(a>b)

                    return a;

                  return b;

        }

        行內函數將整個函式體的程式碼插入呼叫語句處,就像整個函式體在呼叫處被重寫了一遍一樣。行內函數比使用普通函式會使最終可執行程式的體積增加。以時間換取空間或增加空間消耗來節省時間,也是計算機學科中常用的辦法。

        另外需要注意的是,呼叫行內函數的語句前面,必須已經出現行內函數的定義(即整個函式體),而不能只出現行內函數的宣告。

8.函式過載

        一個或多個函式名字相同,然而引數個數或引數型別不相同,這叫做函式的過載。C語言裡沒有函式過載機制,但C++有。

        以下三個函式是過載的關係:

                int Max(double f1,double f2){}    

                int Max(int n1,int n2){}

                int Max(int n1,int n2,int n3){}

        函式過載使函式命名變得簡單。編譯器根據呼叫函式中的實參個數和型別判斷應該呼叫哪個函式。

        注意:同名函式只有引數不同才算過載,如果兩個同名函式引數表相同而返回值型別不同,不是過載而是重複定義,是補允許的。

9.函式的預設引數

    C++中,定義函式的時候可以讓最右邊的連續若干個引數有預設值,那麼呼叫函式的時候,若相應位置不寫引數,引數就是預設值。

    如:

        void func(int x1,int x2=10,int x3=8){

            func(10);  //等效於func(10,10,8)

            func(10,8);//等效於func(10,8,8)

            func(10,,10); //不行,只能最右邊的連續若干個引數預設。

    預設引數優點:

        (1)編寫函式呼叫語句時可以少輸入引數,尤其在函式引數個數多時能省事兒。

        (2)使程式的可擴充性變好,即程式需要增加新功能時改動儘可能少。如果某個寫好的函式需要新增新的引數,而原先那些呼叫該函式的語句,未必需要使用新增的引數,為了避免那些對原先寫好的呼叫語句的修改,就可以使用預設引數。